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>
349 lines
14 KiB
OpenSCAD
349 lines
14 KiB
OpenSCAD
// =============================================================================
|
||
// 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.
|
||
// =============================================================================
|