saltylab-firmware/chassis/prototype_baseplate.scad
sl-firmware 291dd689f8 feat: remove all STM32/Mamba/BlackPill references — ESP32-S3 only
Archive STM32 firmware to legacy/stm32/:
- src/, include/, lib/USB_CDC/, platformio.ini, test stubs, flash_firmware.py
- test/test_battery_adc.c, test_hw_button.c, test_pid_schedule.c, test_vesc_can.c, test_can_watchdog.c
- USB_CDC_BUG.md

Rename: stm32_protocol → esp32_protocol, mamba_protocol → balance_protocol,
  stm32_cmd_node → esp32_cmd_node, stm32_cmd_params → esp32_cmd_params,
  stm32_cmd.launch.py → esp32_cmd.launch.py,
  test_stm32_protocol → test_esp32_protocol, test_stm32_cmd_node → test_esp32_cmd_node

Content cleanup across all files:
- Mamba F722S → ESP32-S3 BALANCE
- BlackPill → ESP32-S3 IO
- STM32F722/F7xx → ESP32-S3
- stm32Mode/Version/Port → esp32Mode/Version/Port
- STM32 State/Mode labels → ESP32 State/Mode
- Jetson Nano → Jetson Orin Nano Super
- /dev/stm32 → /dev/esp32
- stm32_bridge → esp32_bridge
- STM32 HAL → ESP-IDF

docs/SALTYLAB.md:
- Update "Drone FC Details" to describe ESP32-S3 BALANCE board (Waveshare ESP32-S3 Touch LCD 1.28)
- Replace verbose "Self-Balancing Control" STM32 section with brief note pointing to SAUL-TEE-SYSTEM-REFERENCE.md

TEAM.md: Update Embedded Firmware Engineer role to ESP32-S3 / ESP-IDF

No new functionality — cleanup only.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 08:41:26 -04:00

349 lines
14 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;
// FC mount — ESP32-S3 BALANCE 30.5 × 30.5 mm M3
FC_PITCH = 30.5;
FC_HOLE_D = 3.2;
// FC 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);
// ── FC mount (ESP32-S3 BALANCE 30.5 × 30.5 M3) ────────────────────────
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.
// =============================================================================