saltylab-firmware/chassis/chassis_frame.scad
sl-firmware fa75c442a7 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 09:00:38 -04:00

341 lines
16 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 — Parametric Chassis Frame
// Task: bd-1iy5
// Agent: sl-mechanical
// Date: 2026-02-28
//
// Self-balancing two-wheeled robot chassis
// Requirements:
// - 600mm wheelbase
// - 2x hoverboard hub motors (170mm OD)
// - ESP32-S3 ESP32-S3 BALANCE FC mount (30.5x30.5mm pattern)
// - Battery tray (24V 4Ah — ~180x70x50mm pack)
// - Jetson Orin Nano Super B01 mount plate (100x80mm, M3 holes)
// - Front/rear bumper brackets
// =============================================================================
// ─── RENDER QUALITY ──────────────────────────────────────────────────────────
$fn = 64;
// =============================================================================
// PARAMETERS — edit here to adjust the whole model
// =============================================================================
// ── Wheelbase / overall geometry ─────────────────────────────────────────────
WHEELBASE = 600; // mm, center-to-center axle distance (lateral)
FRAME_WIDTH = 220; // mm, front-to-back depth of main deck
DECK_THICKNESS = 6; // mm, main deck plate thickness
WALL_T = 4; // mm, general wall thickness for brackets/ribs
// ── Hub motor parameters ─────────────────────────────────────────────────────
MOTOR_OD = 170; // mm, motor housing outer diameter
MOTOR_AXLE_D = 14; // mm, axle bolt diameter
MOTOR_AXLE_FLAT = 10; // mm, axle flat-to-flat (for anti-rotation)
MOTOR_FORK_WIDTH = 24; // mm, dropout slot width (fits 10-14mm axle + spacers)
MOTOR_FORK_DEPTH = 60; // mm, dropout slot depth from fork tip
MOTOR_FORK_H = 80; // mm, total height of motor fork bracket
MOTOR_FORK_T = 8; // mm, fork plate thickness
AXLE_HEIGHT = 310; // mm, axle CL above ground (motor radius + clearance)
// ── FC mount (ESP32-S3 BALANCE — 30.5 × 30.5 mm M3 pattern) ──────────────────────
FC_MOUNT_SPACING = 30.5; // mm, hole pattern pitch
FC_MOUNT_HOLE_D = 3.2; // mm, M3 clearance
FC_STANDOFF_H = 6; // mm, standoff height
FC_PAD_T = 3; // mm, mounting pad thickness
// ── Battery tray (24V 4Ah LiPo / LiFePO4) ───────────────────────────────────
BATT_L = 185; // mm, cell pack length
BATT_W = 72; // mm, cell pack width
BATT_H = 52; // mm, cell pack height
BATT_WALL = 3; // mm, tray wall thickness
BATT_FLOOR = 4; // mm, tray floor thickness
BATT_STRAP_W = 20; // mm, Velcro strap slot width
BATT_STRAP_T = 2; // mm, strap slot depth
// ── Jetson Orin Nano Super B01 mount plate ──────────────────────────────────────────────
// B01 carrier board hole pattern: 58 x 58 mm M3 (inner) + corner pass-throughs
JETSON_HOLE_PITCH = 58; // mm, M3 mounting hole pattern
JETSON_HOLE_D = 3.2; // mm
JETSON_PLATE_L = 105; // mm, plate length
JETSON_PLATE_W = 90; // mm, plate width
JETSON_PLATE_T = 4; // mm, plate thickness
JETSON_STANDOFF_H = 8; // mm
// ── Bumper bracket ────────────────────────────────────────────────────────────
BUMPER_L = WHEELBASE + 60; // mm, bumper rail length (overhangs wheel CL)
BUMPER_H = 40; // mm, bracket vertical height
BUMPER_T = 5; // mm, bracket plate thickness
BUMPER_TUBE_OD = 22; // mm, 3/4" EMT conduit OD for bumper rail
// ── Rib / gusset parameters ───────────────────────────────────────────────────
RIB_W = 20; // mm, longitudinal rib width
RIB_H = 40; // mm, rib height below deck
RIB_T = 4; // mm, rib plate thickness (laser/router cut)
// ── Fastener helpers ─────────────────────────────────────────────────────────
M3_D = 3.2;
M4_D = 4.3;
M5_D = 5.3;
M6_D = 6.5;
// =============================================================================
// MAIN ASSEMBLY — comment/uncomment parts as needed
// =============================================================================
color("Silver", 0.9) main_deck();
color("DimGray") motor_fork(side= 1); // right (+X)
color("DimGray") motor_fork(side=-1); // left (-X)
color("SteelBlue") battery_tray();
color("OliveDrab") fc_mount_plate();
color("DarkOrange") jetson_mount_plate();
color("Tomato") bumper_bracket(front= 1);
color("Tomato") bumper_bracket(front=-1);
color("WhiteSmoke") longitudinal_ribs();
// =============================================================================
// MODULES
// =============================================================================
// ─── Main deck plate ─────────────────────────────────────────────────────────
module main_deck() {
difference() {
// Deck plate
translate([-WHEELBASE/2 - MOTOR_FORK_T, -FRAME_WIDTH/2, 0])
cube([WHEELBASE + 2*MOTOR_FORK_T, FRAME_WIDTH, DECK_THICKNESS]);
// Lightening holes — 3 rows × 4 cols
for (x = [-WHEELBASE/3, 0, WHEELBASE/3])
for (y = [-FRAME_WIDTH/4, FRAME_WIDTH/4])
translate([x, y, -1])
cylinder(d=50, h=DECK_THICKNESS+2);
// FC mount holes
fc_mount_holes(z_offset=-1, depth=DECK_THICKNESS+2);
// Battery tray floor cutout for wire pass-through
translate([-BATT_L/2 + 40, -BATT_W/2 + 40, -1])
cube([BATT_L - 80, BATT_W - 80, DECK_THICKNESS+2]);
// Cable routing slots
for (x = [-60, 60])
translate([x, -FRAME_WIDTH/2 - 1, -1])
cube([14, 18, DECK_THICKNESS+2]);
}
}
// ─── Longitudinal ribs (×2, symmetric F/R) ───────────────────────────────────
module longitudinal_ribs() {
for (y = [-FRAME_WIDTH/2 + RIB_W/2 + WALL_T,
FRAME_WIDTH/2 - RIB_W/2 - WALL_T])
translate([-WHEELBASE/2, y - RIB_T/2, -RIB_H])
cube([WHEELBASE, RIB_T, RIB_H]);
}
// ─── Motor fork dropout bracket ──────────────────────────────────────────────
// Mounts to deck edge; provides a CNC-milled or FDM dropout slot for the axle.
// `side` = +1 (right/+X) or -1 (left/-X)
module motor_fork(side = 1) {
x_pos = side * (WHEELBASE/2);
translate([x_pos, 0, 0]) {
difference() {
union() {
// Vertical fork body
translate([side*(DECK_THICKNESS/2), -MOTOR_FORK_WIDTH/2 - 4, -MOTOR_FORK_H])
cube([side * MOTOR_FORK_T, MOTOR_FORK_WIDTH + 8, MOTOR_FORK_H + DECK_THICKNESS]);
// Gusset triangles
for (g = [-1, 1])
translate([side*(DECK_THICKNESS/2), g*(MOTOR_FORK_WIDTH/2 + 2), -MOTOR_FORK_H])
linear_extrude(height=MOTOR_FORK_T)
polygon([[0,0],[0, g*20],[side*-30, 0]]);
}
// Axle dropout slot (open at bottom)
translate([side*(DECK_THICKNESS/2) - 1,
-MOTOR_AXLE_FLAT/2,
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2])
cube([MOTOR_FORK_T + 2, MOTOR_AXLE_FLAT, MOTOR_FORK_DEPTH + 1]);
// Axle through-hole at slot top
translate([side*(DECK_THICKNESS/2) - 1, 0,
-MOTOR_FORK_DEPTH - MOTOR_AXLE_D/2])
rotate([0, 90, 0])
cylinder(d=MOTOR_AXLE_D + 1, h=MOTOR_FORK_T + 2);
// Bolt holes for deck attachment (M5 × 4)
for (y = [-20, 20])
for (z = [4, 12])
translate([side*(DECK_THICKNESS/2) - 1, y, z - MOTOR_FORK_H + MOTOR_FORK_H/2])
rotate([0, 90, 0])
cylinder(d=M5_D, h=MOTOR_FORK_T + 2);
}
}
}
// ─── Battery tray ─────────────────────────────────────────────────────────────
// Positioned in centre of deck, recessed 10mm below deck surface
module battery_tray() {
z_base = -BATT_FLOOR - BATT_H - 5; // hang below deck
translate([-BATT_L/2 - BATT_WALL, -BATT_W/2 - BATT_WALL, z_base]) {
difference() {
// Outer tray body
cube([BATT_L + 2*BATT_WALL,
BATT_W + 2*BATT_WALL,
BATT_H + BATT_FLOOR]);
// Inner cavity
translate([BATT_WALL, BATT_WALL, BATT_FLOOR])
cube([BATT_L, BATT_W, BATT_H + 1]);
// Strap slots (2× longitudinal)
for (x = [BATT_L/2 - BATT_STRAP_W*2,
BATT_L/2 + BATT_STRAP_W])
translate([x, -1, BATT_FLOOR + BATT_H/2 - BATT_STRAP_W/2])
cube([BATT_STRAP_W, BATT_W + 2*BATT_WALL + 2, BATT_STRAP_W]);
// Ventilation slots bottom (3×)
for (i = [0:2])
translate([BATT_WALL + 20 + i*50, BATT_WALL + 10, -1])
cube([30, BATT_W - 20, BATT_FLOOR + 2]);
// Mount holes to deck (M4 × 4 corners)
for (x = [10, BATT_L + BATT_WALL - 2])
for (y = [10, BATT_W + BATT_WALL - 2])
translate([x, y, BATT_H + BATT_FLOOR - 1])
cylinder(d=M4_D, h=BATT_FLOOR + 2);
}
}
}
// ─── FC mount holes helper ────────────────────────────────────────────────────
module fc_mount_holes(z_offset=0, depth=10) {
// ESP32-S3 BALANCE: 30.5×30.5 mm M3 pattern, centred at origin
for (x = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
for (y = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
translate([x, y, z_offset])
cylinder(d=FC_MOUNT_HOLE_D, h=depth);
}
// ─── FC mount plate (raised 40mm above deck centre) ──────────────────────────
module fc_mount_plate() {
fc_x = -50; // offset toward front from deck centre
fc_y = 0;
plate_z = DECK_THICKNESS + FC_STANDOFF_H;
translate([fc_x, fc_y, 0]) {
difference() {
union() {
// Mount pads with standoffs
for (x = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2])
for (y = [-FC_MOUNT_SPACING/2, FC_MOUNT_SPACING/2]) {
translate([x, y, DECK_THICKNESS])
cylinder(d=8, h=FC_STANDOFF_H); // standoff column
translate([x - 5, y - 5, DECK_THICKNESS])
cube([10, 10, FC_PAD_T]); // pad base
}
// Base plate
translate([-FC_MOUNT_SPACING/2 - 8, -FC_MOUNT_SPACING/2 - 8, DECK_THICKNESS])
cube([FC_MOUNT_SPACING + 16, FC_MOUNT_SPACING + 16, FC_PAD_T]);
}
// M3 through-holes in standoffs
fc_mount_holes(z_offset=DECK_THICKNESS - 1, depth=FC_STANDOFF_H + FC_PAD_T + 2);
// Deck anchor holes
fc_mount_holes(z_offset=-1, depth=DECK_THICKNESS + 2);
}
}
}
// ─── Jetson Orin Nano Super B01 mount plate ─────────────────────────────────────────────
// Positioned rear of deck, elevated on standoffs
module jetson_mount_plate() {
jet_x = 60; // offset toward rear
jet_y = 0;
plate_z = DECK_THICKNESS + JETSON_STANDOFF_H;
translate([jet_x, jet_y, 0]) {
difference() {
union() {
// Mounting plate
translate([-JETSON_PLATE_L/2, -JETSON_PLATE_W/2, DECK_THICKNESS + JETSON_STANDOFF_H])
cube([JETSON_PLATE_L, JETSON_PLATE_W, JETSON_PLATE_T]);
// Four standoff columns
for (x = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
for (y = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
translate([x, y, DECK_THICKNESS])
cylinder(d=6, h=JETSON_STANDOFF_H);
}
// B01 M3 hole pattern (58×58 mm)
for (x = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
for (y = [-JETSON_HOLE_PITCH/2, JETSON_HOLE_PITCH/2])
translate([x, y, DECK_THICKNESS - 1])
cylinder(d=JETSON_HOLE_D, h=JETSON_STANDOFF_H + JETSON_PLATE_T + 2);
// Ventilation / cable routing slots in plate
for (i = [-1, 0, 1])
translate([i*25 - 10, -JETSON_PLATE_W/2 + 10,
DECK_THICKNESS + JETSON_STANDOFF_H - 1])
cube([20, JETSON_PLATE_W - 20, JETSON_PLATE_T + 2]);
// Deck anchor holes (M3 × 4 corners of plate)
for (x = [-JETSON_PLATE_L/2 + 8, JETSON_PLATE_L/2 - 8])
for (y = [-JETSON_PLATE_W/2 + 8, JETSON_PLATE_W/2 - 8])
translate([x, y, -1])
cylinder(d=M3_D, h=DECK_THICKNESS + 2);
}
}
}
// ─── Bumper bracket ───────────────────────────────────────────────────────────
// `front` = +1 (front) or -1 (rear)
module bumper_bracket(front = 1) {
y_pos = front * (FRAME_WIDTH/2);
translate([0, y_pos, 0]) {
difference() {
union() {
// Horizontal mounting rail plate
translate([-BUMPER_L/2, front*(BUMPER_T/2), 0])
cube([BUMPER_L, BUMPER_T, BUMPER_H]);
// Vertical gussets at ends
for (x = [-BUMPER_L/2 + 5, BUMPER_L/2 - BUMPER_T - 5])
translate([x, front*(BUMPER_T), 0])
cube([BUMPER_T, 20, BUMPER_H]);
// Bumper tube saddle clamps (×3 evenly spaced)
for (x = [-BUMPER_L/3, 0, BUMPER_L/3])
translate([x - BUMPER_TUBE_OD/2 - 2,
front*(BUMPER_T + 15),
BUMPER_H - BUMPER_TUBE_OD/2 - 5])
difference() {
cube([BUMPER_TUBE_OD + 4, BUMPER_TUBE_OD/2 + 4, BUMPER_TUBE_OD + 4]);
translate([BUMPER_TUBE_OD/2 + 2, -1, BUMPER_TUBE_OD/2 + 2])
rotate([-90, 0, 0])
cylinder(d=BUMPER_TUBE_OD, h=BUMPER_TUBE_OD/2 + 6);
}
}
// Deck mounting holes (M5 × 6 along length)
for (x = [-BUMPER_L/2 + 20, -BUMPER_L/6, BUMPER_L/6, BUMPER_L/2 - 20])
translate([x, y_pos * 0.001, -1])
cylinder(d=M5_D, h=BUMPER_H + 2);
}
}
}
// =============================================================================
// DIMENSIONS / ANNOTATIONS (2D cross-section reference)
// =============================================================================
// Uncomment to render a side-view dimension guide:
//
// %translate([0, FRAME_WIDTH/2 + 30, 0]) {
// color("Blue") {
// cube([WHEELBASE, 1, 1]); // wheelbase span
// translate([0,0,0]) text(str("Wheelbase: ", WHEELBASE, "mm"), size=12);
// }
// }