saltylab-firmware/chassis/prototype_baseplate.scad
sl-mechanical a2c554c232 cleanup: remove all Mamba/F722S/STM32F722 refs — replace with ESP32-S3 BALANCE/IO
- docs/: rewrite AGENTS.md, wiring-diagram.md (SAUL-TEE arch); update
  SALTYLAB.md, FACE_LCD_ANIMATION.md, board-viz.html, SALTYLAB-DETAILED refs
- cad/: dimensions.scad FC params → ESP32-S3 BALANCE params
- chassis/: ASSEMBLY.md, BOM.md, ip54_BOM.md, *.scad — FC_MOUNT_SPACING/
  FC_PITCH → TBD ESP32-S3; Drone FC → MCU mount throughout
- CLAUDE.md, TEAM.md: project desc → SAUL-TEE; hardware table → ESP32-S3/VESC
- USB_CDC_BUG.md: marked ARCHIVED (legacy STM32 era)
- AUTONOMOUS_ARMING.md: USB CDC → inter-board UART (ESP32-S3 BALANCE)
- projects/saltybot/SLAM-SETUP-PLAN.md: FC/STM32F722 → BALANCE/CAN
- jetson/docs/pinout.md, power-budget.md, README.md: STM32 bridge → CAN bridge
- jetson/config/RECOVERY_BEHAVIORS.md: FC+Hoverboard → BALANCE+VESC
- jetson/ros2_ws: stm32_protocol.py → esp32_protocol.py,
  stm32_cmd_node.py → esp32_cmd_node.py,
  mamba_protocol.py → balance_protocol.py; can_bridge_node imports updated
- scripts/flash_firmware.py: DFU/STM32 → pio run -t upload
- src/ include/: ARCHIVED headers added (legacy code preserved)
- test/: ARCHIVED notices; STM32F722 comments marked LEGACY
- ui/diagnostics_panel.html: Board/STM32 → ESP32-S3

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 09:06:09 -04:00

350 lines
15 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.

// =============================================================================
// SaltyBot — Prototype Base Plate (Rev C — compact, stem-mount)
// Agent: sl-mechanical | 2026-02-28
//
// Laser-cut or CNC-routed flat plate (6 mm Al / 8 mm acrylic).
// Uses CALIPER-VERIFIED hub motor axle measurements (see PR #7 / #11).
//
// ARCHITECTURE CHANGE (Rev C):
// Batteries are NO LONGER on the base plate.
// They stand vertically on a central stem via stem_battery_clamp.scad.
// The base plate is now compact — only axle dropouts + stem socket + FC mount.
//
// ── AXLE PROFILE (stepped D-cut, caliper-verified) ───────────────────────────
// Zone │ Feature │ Ø / Width │ Length
// ───────┼──────────────────┼────────────────────┼──────────
// Base │ Round (near hub) │ Ø 16.11 mm │ 15.00 mm
// D-cut │ Round OD │ Ø 15.95 mm │ 43.35 mm
// │ Flat chord │ 13.00 mm │
// Tip │ Shoulder/end │ — │ 3.00 mm
// Total │ Hub face → tip │ — │ 65.50 mm
// Bearing seat collar: Ø 37.8 mm
// Tire: 10 × 2.125" pneumatic (Ø 254 mm, 35 PSI)
// =============================================================================
$fn = 128;
// =============================================================================
// AXLE PARAMETERS — caliper-verified
// =============================================================================
AXLE_BASE_DIA = 16.11;
AXLE_BASE_LEN = 15.00;
AXLE_DCUT_DIA = 15.95;
AXLE_DCUT_FLAT = 13.00;
AXLE_DCUT_LEN = 43.35;
AXLE_TIP_LEN = 3.00;
AXLE_TOTAL = 65.50;
BEARING_SEAT_OD = 37.80;
TIRE_OD = 254.0;
AXLE_CL_HEIGHT = TIRE_OD / 2; // 127 mm above ground
// =============================================================================
// PLATE PARAMETERS
// =============================================================================
WHEELBASE = 600.0; // mm axle C/L to axle C/L
// Plate depth now driven only by structural + FC needs (no battery footprint).
PLATE_DEPTH = 130.0; // mm front-to-rear
PLATE_OVERHANG = 40.0; // mm plate past axle C/L each side
PLATE_THICK = 6.0; // mm
// Fork slot
FORK_W = AXLE_BASE_DIA + 0.4; // 16.51 mm
FORK_DEPTH = 50.0;
// Dropout clamp (two-piece sandwich)
CLAMP_L = 80.0;
CLAMP_H = 60.0;
CLAMP_THICK = 8.0;
CLAMP_BOLT_D = 5.3; // M5
CLAMP_BOLT_DX = 22.0;
CLAMP_BOLT_DY = 22.0;
CLAMP_ALIGN_D = 4.1; // Ø4 pin
// D-cut bore clearance
DCUT_CL = 0.3;
// MCU mount — ESP32-S3 BALANCE board (Waveshare Touch LCD 1.28)
// ⚠ FC_PITCH TBD — update before machining. See docs/SAUL-TEE-SYSTEM-REFERENCE.md
FC_PITCH = 0.0; // TBD — ESP32-S3 board hole spacing not yet confirmed
FC_HOLE_D = 3.2;
// Board is offset toward front of plate (away from stem)
FC_X_OFFSET = -40.0; // mm from plate centre (negative = front/motor side)
// =============================================================================
// STEM SOCKET PARAMETERS
// =============================================================================
STEM_OD = 38.1; // mm 1.5" EMT conduit OD
STEM_BORE = STEM_OD + 0.5; // 38.6 mm with clearance
// Flange ring (laser-cut, bolts above + below plate to grip tube):
STEM_FLANGE_OD = 82.0; // mm flange outer diameter
STEM_FLANGE_BC = 66.0; // mm bolt circle diameter (4× M5 at 90°)
STEM_FLANGE_T = 6.0; // mm = PLATE_THICK (flush-mount)
// Stem position: at plate centre (X=0, Y=0)
// =============================================================================
// UTILITIES
// =============================================================================
M3 = 3.2; M4 = 4.3; M5 = 5.3;
PLATE_X_HALF = WHEELBASE/2 + PLATE_OVERHANG; // ± 340 mm
DCUT_R = (AXLE_DCUT_DIA + 2*DCUT_CL) / 2;
DCUT_FC = AXLE_DCUT_FLAT + 2*DCUT_CL;
// =============================================================================
// RENDER CONTROL
// =============================================================================
//
// "assembly" full 3-D preview
// "plate_2d" DXF — base plate
// "clamp_lower_2d" DXF — lower dropout clamp (× 2)
// "clamp_upper_2d" DXF — upper dropout clamp (× 2)
// "stem_flange_2d" DXF — stem flange ring (× 2, one above + one below plate)
RENDER = "assembly";
if (RENDER == "assembly") {
assembly();
} else if (RENDER == "plate_2d") {
projection(cut=true) translate([0,0,-PLATE_THICK/2]) base_plate();
} else if (RENDER == "clamp_lower_2d") {
projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_lower();
} else if (RENDER == "clamp_upper_2d") {
projection(cut=true) translate([0,0,-CLAMP_THICK/2]) clamp_upper();
} else if (RENDER == "stem_flange_2d") {
projection(cut=true) translate([0,0,-STEM_FLANGE_T/2]) stem_flange();
}
// =============================================================================
// ASSEMBLY
// =============================================================================
module assembly() {
color("Silver", 0.85) base_plate();
for (side = [-1, 1]) {
color("SteelBlue", 0.80)
translate([side * WHEELBASE/2, 0, PLATE_THICK])
clamp_lower();
color("CornflowerBlue", 0.80)
translate([side * WHEELBASE/2, 0, PLATE_THICK + CLAMP_THICK])
clamp_upper();
}
// Stem flange rings (above and below plate)
color("DimGray", 0.70)
translate([0, 0, -STEM_FLANGE_T])
stem_flange();
color("DimGray", 0.70)
translate([0, 0, PLATE_THICK])
stem_flange();
// Reference ghosts
%color("Orange", 0.25)
translate([0, 0, PLATE_THICK + STEM_FLANGE_T])
cylinder(d=STEM_OD, h=800); // vertical stem
%for (side = [-1, 1])
color("Tomato", 0.2)
translate([side * WHEELBASE/2, 0, 0])
rotate([0, side*90, 0])
axle_ghost();
}
// =============================================================================
// BASE PLATE (Part A — compact)
// =============================================================================
module base_plate() {
R = 12; // corner radius
difference() {
// ── Outer profile ──
linear_extrude(PLATE_THICK)
minkowski() {
square([2*(PLATE_X_HALF - R), PLATE_DEPTH - 2*R], center=true);
circle(r=R);
}
// ── Fork slots (open at ±X edges, semicircular tip) ──────────────
for (side = [-1, 1]) {
translate([side*(PLATE_X_HALF - FORK_DEPTH), -FORK_W/2, -1])
cube([FORK_DEPTH + 1, FORK_W, PLATE_THICK + 2]);
translate([side*(PLATE_X_HALF - FORK_DEPTH), 0, -1])
cylinder(d=FORK_W, h=PLATE_THICK + 2);
}
// ── Bearing seat relief (prevents Ø37.8 mm collar binding on edge) ─
for (side = [-1, 1])
translate([side*PLATE_X_HALF - side*(BEARING_SEAT_OD/2 + 1),
-BEARING_SEAT_OD/2, -1])
cube([BEARING_SEAT_OD/2 + 2, BEARING_SEAT_OD, PLATE_THICK + 2]);
// ── Dropout clamp bolt through-holes ─────────────────────────────
for (side = [-1, 1])
for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
translate([side*WHEELBASE/2 + dx, dy, -1])
cylinder(d=CLAMP_BOLT_D, h=PLATE_THICK + 2);
// ── Alignment pin holes (Ø4) ──────────────────────────────────────
for (side = [-1, 1])
for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8])
translate([side*WHEELBASE/2, dy, -1])
cylinder(d=CLAMP_ALIGN_D, h=PLATE_THICK + 2);
// ── Stem socket bore ──────────────────────────────────────────────
translate([0, 0, -1])
cylinder(d=STEM_BORE, h=PLATE_THICK + 2);
// ── Stem flange bolt holes (4× M5, 90° pattern on STEM_FLANGE_BC) ─
for (a = [0, 90, 180, 270])
rotate([0, 0, a])
translate([STEM_FLANGE_BC/2, 0, -1])
cylinder(d=M5, h=PLATE_THICK + 2);
// ── MCU mount (ESP32-S3 BALANCE — hole spacing TBD) ──────────────
for (x = [FC_X_OFFSET - FC_PITCH/2, FC_X_OFFSET + FC_PITCH/2])
for (y = [-FC_PITCH/2, FC_PITCH/2])
translate([x, y, -1])
cylinder(d=FC_HOLE_D, h=PLATE_THICK + 2);
// ── Wiring / cable pass-through slots (2×, flanking stem) ─────────
for (dy = [-30, 30])
hull() {
translate([15, dy, -1]) cylinder(d=14, h=PLATE_THICK + 2);
translate([-15, dy, -1]) cylinder(d=14, h=PLATE_THICK + 2);
}
// ── Lightening slots (between FC zone and dropout zones) ──────────
for (sx = [-1, 1]) {
// One slot each side of stem, in the structural corridor
lx = sx * (WHEELBASE/4);
hull() {
translate([lx, -(PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2);
translate([lx, (PLATE_DEPTH/2 - 22), -1]) cylinder(d=18, h=PLATE_THICK+2);
}
}
}
}
// =============================================================================
// STEM FLANGE RING (laser-cut, qty 2 — one above, one below plate)
// =============================================================================
module stem_flange() {
difference() {
cylinder(d=STEM_FLANGE_OD, h=STEM_FLANGE_T);
// Stem bore (tight fit — tube presses into flange)
translate([0, 0, -1])
cylinder(d=STEM_BORE, h=STEM_FLANGE_T + 2);
// 4× M5 flange bolts
for (a = [0, 90, 180, 270])
rotate([0, 0, a])
translate([STEM_FLANGE_BC/2, 0, -1])
cylinder(d=M5, h=STEM_FLANGE_T + 2);
}
}
// =============================================================================
// DROPOUT CLAMP — LOWER (round bore, base-section diameter)
// =============================================================================
module clamp_lower() {
difference() {
hull() {
translate([-CLAMP_L/2 + CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
translate([ CLAMP_L/2 - CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
}
// Round bore (base zone)
translate([0, 0, -1]) cylinder(d=AXLE_BASE_DIA + 0.4, h=CLAMP_THICK + 2);
// Slide-in slot (open toward wheel side)
translate([-CLAMP_L/2 - 1, -FORK_W/2, -1])
cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]);
// Clamp bolts
for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2);
// Alignment pins
for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8])
translate([0, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2);
}
}
// =============================================================================
// DROPOUT CLAMP — UPPER (D-cut bore, anti-rotation)
// =============================================================================
module clamp_upper() {
dcut_r = DCUT_R;
dcut_d = sqrt(pow(dcut_r, 2) - pow(DCUT_FC/2, 2));
difference() {
hull() {
translate([-CLAMP_L/2 + CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
translate([ CLAMP_L/2 - CLAMP_H/2, 0, 0]) cylinder(d=CLAMP_H, h=CLAMP_THICK);
}
// D-cut bore
translate([0, 0, -1])
linear_extrude(CLAMP_THICK + 2)
dcut_profile_2d(dcut_r, dcut_d);
// Slide-in slot
translate([-CLAMP_L/2 - 1, -FORK_W/2, -1])
cube([CLAMP_L/2 + 1, FORK_W, CLAMP_THICK + 2]);
// Clamp bolts
for (dx = [-CLAMP_BOLT_DX, CLAMP_BOLT_DX])
for (dy = [-CLAMP_BOLT_DY, CLAMP_BOLT_DY])
translate([dx, dy, -1]) cylinder(d=CLAMP_BOLT_D, h=CLAMP_THICK + 2);
// Alignment pins
for (dy = [-CLAMP_BOLT_DY + 8, CLAMP_BOLT_DY - 8])
translate([0, dy, -1]) cylinder(d=CLAMP_ALIGN_D, h=CLAMP_THICK + 2);
// Orientation emboss
translate([0, dcut_d + 1.5, CLAMP_THICK - 0.8])
linear_extrude(1)
polygon([[0,0],[-3,-5],[3,-5]]);
}
}
// ── D-cut 2D profile helper ───────────────────────────────────────────────────
module dcut_profile_2d(r, flat_d) {
intersection() {
circle(r=r);
translate([-r - 1, -r - 1])
square([2*(r+1), r + 1 + flat_d]);
}
}
// =============================================================================
// AXLE GHOST (visualisation only)
// =============================================================================
module axle_ghost() {
cylinder(d=BEARING_SEAT_OD, h=12);
translate([0,0,12]) cylinder(d=AXLE_BASE_DIA, h=AXLE_BASE_LEN);
translate([0,0,12+AXLE_BASE_LEN]) cylinder(d=AXLE_DCUT_DIA, h=AXLE_DCUT_LEN);
}
// =============================================================================
// DXF EXPORT
// =============================================================================
//
// Part 1 — Base plate:
// openscad prototype_baseplate.scad -D 'RENDER="plate_2d"' -o baseplate.dxf
//
// Part 2 — Dropout clamp, lower (× 2):
// openscad prototype_baseplate.scad -D 'RENDER="clamp_lower_2d"' -o clamp_lower.dxf
//
// Part 3 — Dropout clamp, upper (× 2):
// openscad prototype_baseplate.scad -D 'RENDER="clamp_upper_2d"' -o clamp_upper.dxf
//
// Part 4 — Stem flange ring (× 2, one each side of plate):
// openscad prototype_baseplate.scad -D 'RENDER="stem_flange_2d"' -o stem_flange.dxf
//
// Materials:
// Plate + flanges : 6 mm 5052-H32 aluminium (preferred)
// 8 mm clear acrylic (quick proto)
// Dropout clamps : 8 mm 6061-T6 aluminium
// Stem tube : 38.1 mm OD × 1.5 mm wall 6061-T6 (or 1.5" EMT)
// Cut stem to ~1050 mm — allows batteries from ~100 mm to ~950 mm height.
// =============================================================================