// ============================================================
// vesc_mount.scad — FSESC 6.7 Pro Mini Dual ESC Mount Cradle
// Issue #699 / sl-mechanical  2026-03-17
// ============================================================
// Open-top tray for Flipsky FSESC 6.7 Pro Mini Dual (~100 × 68 × 28 mm).
// Attaches to 2020 aluminium T-slot rail via 4× M5 T-nuts
// (2× per rail, two parallel rails, 60 mm bolt spacing in X,
//  20 mm bolt spacing in Y matching 2020 slot pitch).
//
// Connector access:
//   XT60 battery inputs    — X− end wall cutouts (2 connectors, side-by-side)
//   XT30 motor outputs     — Y+ and Y− side wall cutouts (2 per side wall)
//   CAN/UART terminal      — X+ end wall cutout (screw terminal, wire exit)
//
// Ventilation:
//   Open top face          — heatsink fins fully exposed
//   Floor grille slots     — under-board airflow
//   Side vent louvres      — 4 slots on each Y± wall at heatsink height
//
// Retention: 4× M3 heat-set insert boss in floor — board screws down through
// ESC mounting holes via M3×8 FHCS. Board sits on 4 mm raised posts for
// under-board airflow.
//
// ⚠  VERIFY WITH CALIPERS BEFORE PRINTING:
//    PCB_L, PCB_W                   board outline
//    XT60_W, XT60_H                 XT60 shell at X− edge
//    XT30_W, XT30_H                 XT30 shells at Y± edges
//    TERM_W, TERM_H                 CAN screw terminal at X+ edge
//    MOUNT_X1/X2, MOUNT_Y1/Y2      ESC board mounting hole pattern
//
// Print settings (PETG):
//   3 perimeters, 40 % gyroid infill, no supports, 0.2 mm layer
//   Print orientation: open face UP (as modelled)
//
// BOM:
//   4 × M5×10 BHCS  +  4 × M5 slide-in T-nut  (2020 rail)
//   4 × M3 heat-set insert (Voron-style, OD 4.5 mm × 4 mm deep)
//   4 × M3×8 FHCS  (board retention)
//
// Export commands:
//   openscad -D 'RENDER="mount"'    -o vesc_mount.stl    vesc_mount.scad
//   openscad -D 'RENDER="assembly"' -o vesc_assembly.png vesc_mount.scad
// ============================================================

RENDER = "assembly"; // mount | assembly

$fn = 48;
EPS = 0.01;

// ── ⚠ Verify before printing ─────────────────────────────────
// FSESC 6.7 Pro Mini Dual PCB
PCB_L    = 100.0;  // board length (X: XT60 end → CAN terminal end)
PCB_W    =  68.0;  // board width  (Y)
PCB_T    =  2.0;   // board thickness (incl. bottom-side components)
COMP_H   =  26.0;  // tallest component above board top face (heatsink ~26 mm)

// XT60 battery connectors at X− end (2 connectors, side-by-side)
XT60_W   =  16.0;  // each XT60 shell width (Y)
XT60_H   =  16.0;  // each XT60 shell height (Z) above board surface
XT60_Z0  =   0.0;  // connector bottom offset above board surface
// Y centres of each XT60 measured from PCB Y− edge
XT60_Y1  =  16.0;
XT60_Y2  =  52.0;

// XT30 motor output connectors at Y± sides (2 per side)
XT30_W   =  10.5;  // each XT30 shell width (X span)
XT30_H   =  12.0;  // each XT30 shell height (Z) above board surface
XT30_Z0  =   0.5;  // connector bottom offset above board surface
// X centres measured from PCB X− edge (same layout both Y− and Y+ sides)
XT30_X1  =  22.0;
XT30_X2  =  78.0;

// CAN / UART screw terminal block at X+ end (3-pos 3.5 mm pitch)
TERM_W   =  14.0;  // terminal block Y span
TERM_H   =  10.0;  // terminal block height above board surface
TERM_Z0  =   0.5;  // terminal bottom offset above board surface
TERM_Y_CTR = PCB_W / 2;

// ── ESC board mounting hole pattern ──────────────────────────
// 4 corner holes, 4 mm inset from each PCB edge
MOUNT_INSET  =  4.0;
MOUNT_X1     = MOUNT_INSET;
MOUNT_X2     = PCB_L - MOUNT_INSET;
MOUNT_Y1     = MOUNT_INSET;
MOUNT_Y2     = PCB_W - MOUNT_INSET;

M3_INSERT_OD =  4.6;   // Voron M3 heat-set insert press-fit OD
M3_INSERT_H  =  4.0;   // insert depth
M3_CLEAR_D   =  3.4;   // M3 clearance bore below insert

// ── Cradle geometry ──────────────────────────────────────────
WALL_T   =  2.8;   // side / end wall thickness
FLOOR_T  =  4.5;   // floor plate thickness (fits M5 BHCS head pocket)
POST_H   =  4.0;   // standoff post height (board lifts off floor for airflow)
CL_SIDE  =  0.35;  // Y clearance per side
CL_END   =  0.40;  // X clearance per end

INN_W    = PCB_W + 2*CL_SIDE;
INN_L    = PCB_L + 2*CL_END;
INN_H    = POST_H + PCB_T + COMP_H + 1.5;

OTR_W    = INN_W  + 2*WALL_T;
OTR_L    = INN_L  + 2*WALL_T;
OTR_H    = FLOOR_T + INN_H;

PCB_X0   = WALL_T + CL_END;
PCB_Y0   = WALL_T + CL_SIDE;
PCB_Z0   = FLOOR_T + POST_H;

// ── M5 T-nut mount (2020 rail) ────────────────────────────────
// 4 bolts: 2 columns (X) × 2 rows (Y), centred on body
M5_D       =  5.3;
M5_HEAD_D  =  9.5;
M5_HEAD_H  =  3.0;
M5_SPAC_X  = 60.0;   // X bolt spacing
M5_SPAC_Y  = 20.0;   // Y bolt spacing (2020 T-slot pitch)

// ── Floor ventilation grille ──────────────────────────────────
GRILLE_SLOT_W =  4.0;
GRILLE_SLOT_T = FLOOR_T - 1.5;
GRILLE_PITCH  = 10.0;
GRILLE_X0     = WALL_T + 14;
GRILLE_X_LEN  = OTR_L - 2*WALL_T - 28;
GRILLE_N      = floor((INN_W - 10) / GRILLE_PITCH);

// ── Side vent louvres on Y± walls ────────────────────────────
LOUV_H     =  5.0;
LOUV_W     = 12.0;
LOUV_Z     = FLOOR_T + POST_H + PCB_T + 4.0;  // mid-heatsink height
LOUV_N     =  4;
LOUV_PITCH = (OTR_L - 2*WALL_T - 20) / max(LOUV_N - 1, 1);

// ── CAN wire strain relief bosses (X+ end) ───────────────────
SR_BOSS_OD =  7.0;
SR_BOSS_H  =  6.0;
SR_SLOT_W  =  3.5;
SR_SLOT_T  =  2.2;
SR_Y1      = WALL_T + INN_W * 0.25;
SR_Y2      = WALL_T + INN_W * 0.75;
SR_X       = OTR_L - WALL_T - SR_BOSS_OD/2 - 2.5;

// ─────────────────────────────────────────────────────────────
module m3_insert_boss() {
    // Solid post with heat-set insert bore from top
    post_h = FLOOR_T + POST_H;
    difference() {
        cylinder(d = M3_INSERT_OD + 3.2, h = post_h);
        // Insert bore from top
        translate([0, 0, post_h - M3_INSERT_H])
            cylinder(d = M3_INSERT_OD, h = M3_INSERT_H + EPS);
        // Clearance bore from bottom
        translate([0, 0, -EPS])
            cylinder(d = M3_CLEAR_D, h = post_h - M3_INSERT_H + EPS);
    }
}

module vesc_mount() {
    difference() {
        union() {
            // Main body
            cube([OTR_L, OTR_W, OTR_H]);

            // M3 insert bosses at board mounting corners
            for (mx = [MOUNT_X1, MOUNT_X2])
            for (my = [MOUNT_Y1, MOUNT_Y2])
                translate([PCB_X0 + mx, PCB_Y0 + my, 0])
                    m3_insert_boss();

            // CAN strain relief bosses on X+ end
            for (sy = [SR_Y1, SR_Y2])
                translate([SR_X, sy, 0])
                    cylinder(d = SR_BOSS_OD, h = SR_BOSS_H);
        }

        // ── Interior cavity (open top) ─────────────────────────
        translate([WALL_T, WALL_T, FLOOR_T])
            cube([INN_L, INN_W, INN_H + EPS]);

        // ── XT60 cutouts at X− end (2 connectors) ──────────────
        for (yc = [XT60_Y1, XT60_Y2])
            translate([-EPS,
                       PCB_Y0 + yc - (XT60_W + 2.0)/2,
                       PCB_Z0 + XT60_Z0 - 0.5])
                cube([WALL_T + 2*EPS, XT60_W + 2.0, XT60_H + 3.0]);

        // ── XT30 cutouts at Y− side (2 connectors) ─────────────
        for (xc = [XT30_X1, XT30_X2])
            translate([PCB_X0 + xc - (XT30_W + 2.0)/2,
                       -EPS,
                       PCB_Z0 + XT30_Z0 - 0.5])
                cube([XT30_W + 2.0, WALL_T + 2*EPS, XT30_H + 3.0]);

        // ── XT30 cutouts at Y+ side (2 connectors) ─────────────
        for (xc = [XT30_X1, XT30_X2])
            translate([PCB_X0 + xc - (XT30_W + 2.0)/2,
                       OTR_W - WALL_T - EPS,
                       PCB_Z0 + XT30_Z0 - 0.5])
                cube([XT30_W + 2.0, WALL_T + 2*EPS, XT30_H + 3.0]);

        // ── CAN terminal cutout at X+ end ──────────────────────
        translate([OTR_L - WALL_T - EPS,
                   PCB_Y0 + TERM_Y_CTR - (TERM_W + 3.0)/2,
                   PCB_Z0 + TERM_Z0 - 0.5])
            cube([WALL_T + 2*EPS, TERM_W + 3.0, TERM_H + 5.0]);

        // ── Floor ventilation grille ───────────────────────────
        for (i = [0 : GRILLE_N - 1]) {
            sy = WALL_T + 5 + i * GRILLE_PITCH;
            translate([GRILLE_X0, sy, -EPS])
                cube([GRILLE_X_LEN, GRILLE_SLOT_W, GRILLE_SLOT_T + EPS]);
        }

        // ── Side vent louvres — Y− wall ────────────────────────
        for (i = [0 : LOUV_N - 1]) {
            lx = WALL_T + 10 + i * LOUV_PITCH;
            translate([lx, -EPS, LOUV_Z])
                cube([LOUV_W, WALL_T + 2*EPS, LOUV_H]);
        }

        // ── Side vent louvres — Y+ wall ────────────────────────
        for (i = [0 : LOUV_N - 1]) {
            lx = WALL_T + 10 + i * LOUV_PITCH;
            translate([lx, OTR_W - WALL_T - EPS, LOUV_Z])
                cube([LOUV_W, WALL_T + 2*EPS, LOUV_H]);
        }

        // ── M5 BHCS head pockets (4 bolts, bottom face) ────────
        for (mx = [OTR_L/2 - M5_SPAC_X/2, OTR_L/2 + M5_SPAC_X/2])
        for (my = [OTR_W/2 - M5_SPAC_Y/2, OTR_W/2 + M5_SPAC_Y/2])
            translate([mx, my, -EPS]) {
                cylinder(d = M5_D,      h = FLOOR_T + 2*EPS);
                cylinder(d = M5_HEAD_D, h = M5_HEAD_H + EPS);
            }

        // ── Zip-tie slots through CAN strain relief bosses ─────
        for (sy = [SR_Y1, SR_Y2])
            translate([SR_X, sy, SR_BOSS_H/2 - SR_SLOT_T/2])
                rotate([0, 90, 0])
                    cube([SR_SLOT_T, SR_SLOT_W, SR_BOSS_OD + 2*EPS],
                         center = true);

        // ── Weight-relief pocket in floor underside ─────────────
        translate([WALL_T + 16, WALL_T + 6, -EPS])
            cube([OTR_L - 2*WALL_T - 32, OTR_W - 2*WALL_T - 12,
                  FLOOR_T - 2.0 + EPS]);
    }
}

// ── Assembly preview ─────────────────────────────────────────
if (RENDER == "assembly") {
    color("DimGray", 0.93) vesc_mount();

    // Phantom PCB
    color("ForestGreen", 0.30)
        translate([PCB_X0, PCB_Y0, PCB_Z0])
            cube([PCB_L, PCB_W, PCB_T]);

    // Phantom heatsink / component block
    color("SlateGray", 0.22)
        translate([PCB_X0, PCB_Y0, PCB_Z0 + PCB_T])
            cube([PCB_L, PCB_W, COMP_H]);

    // XT60 connector highlights (X− end)
    for (yc = [XT60_Y1, XT60_Y2])
        color("Gold", 0.85)
            translate([-2,
                       PCB_Y0 + yc - XT60_W/2,
                       PCB_Z0 + XT60_Z0])
                cube([WALL_T + 3, XT60_W, XT60_H]);

    // XT30 connector highlights — Y− side
    for (xc = [XT30_X1, XT30_X2])
        color("OrangeRed", 0.80)
            translate([PCB_X0 + xc - XT30_W/2,
                       -2,
                       PCB_Z0 + XT30_Z0])
                cube([XT30_W, WALL_T + 3, XT30_H]);

    // XT30 connector highlights — Y+ side
    for (xc = [XT30_X1, XT30_X2])
        color("OrangeRed", 0.80)
            translate([PCB_X0 + xc - XT30_W/2,
                       OTR_W - WALL_T - 1,
                       PCB_Z0 + XT30_Z0])
                cube([XT30_W, WALL_T + 3, XT30_H]);

    // CAN terminal highlight
    color("Tomato", 0.75)
        translate([OTR_L - WALL_T - 1,
                   PCB_Y0 + TERM_Y_CTR - TERM_W/2,
                   PCB_Z0 + TERM_Z0])
            cube([WALL_T + 3, TERM_W, TERM_H]);

} else {
    vesc_mount();
}
