holder: expose all 59 scad parameters auto-extracted from the .scad source

The bundled hex_cell.scad has many more knobs than the dozen we used to
expose (box clearances, cap/box wall, vertical stacking, busbar template,
etc). Maintaining a hand-curated PARAMS list in Python next to a SCAD file
that already documents every variable inline was always going to drift.

Sync + auto-extract approach:
- scad/hex_cell.scad: replace with the upstream master file from
  Albert Phan's Hex-Cell-Holder fork (adds the BUSBAR TEMPLATE section
  and the "busbar template" part option).
- holder.py: PARAMS now built at import time by _scan_scad(), which
  regexes top-level `name = literal;  // help` lines into Param entries
  up until the // END OF CONFIGURATION marker. Scan handles bool /
  number / string literals; derived expressions and helper variables
  are skipped automatically.
- Manual maps stay small and explicit: _SELECT_OPTIONS for the few
  string-enum params (part, pack_style, box_style, template_outline,
  template_hole_style, etc.), _GROUP_RULES for UI sectioning, and
  _NUMBER_HINTS for sensible min/max/step on the most-tweaked numbers.

UI:
- holder-app.js: extra GROUP_ORDER / GROUP_LABELS for the new
  sections (cap, box, insulator, bolts, wires, stacking, template,
  advanced). Group titles are now click-to-collapse; the advanced
  groups start collapsed so the form isn't a wall of inputs on load.
- holder.css: caret marker on the group title, smooth rotate on
  collapse, hides the body via .collapsed class.

Net effect: every variable in the .scad — including the new busbar
template knobs — is editable from the page, with helpful comments
copied straight from the .scad source.
This commit is contained in:
wenil
2026-05-25 12:44:45 +03:00
parent dfef1453aa
commit 5bba0e3c4a
4 changed files with 337 additions and 55 deletions
+152 -13
View File
@@ -15,16 +15,16 @@
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
cell_dia = 21.2; // Cell diameter default = 18.4 for 18650s **PRINT OUT TEST FIT PIECE STL FIRST**
cell_dia = 21.4; // Cell diameter default = 18.4 for 18650s **PRINT OUT TEST FIT PIECE STL FIRST**
cell_height = 70; // Cell height default = 65 for 18650s
wall = 0.8; // Wall thickness around a single cell. Make as a multiple of the nozzle diameter. Spacing between cells is twice this amount. default = 1.2
wall = 0.2; // Wall thickness around a single cell. Make as a multiple of the nozzle diameter. Spacing between cells is twice this amount. default = 1.2
// If using bought injection molded hexes and printing out the boxes, take the distance between the centers of 2 cells and divide by two for the wall thickness (((((pitch - diameter)/2). Add space for the protuding interlocking tabs in the cap or box clearances.
num_rows = 6;
num_cols = 12;
num_rows = 7;
num_cols = 3;
holder_height = 10; // Height of cell holder default = 10 (not including slot_height)
slot_height = 3; // Height of all slots default = 3 mm (set to 0 for no slots but that allows you to print without support)
slot_height = 1; // Height of all slots default = 3 mm (set to 0 for no slots but that allows you to print without support)
col_slot_width = 8; // Width of slots between rows default = 8
row_slot_width = 8; // Width of slots along rows default = 8
@@ -35,39 +35,40 @@ pack_style = "rect"; // "rect" for rectangular pack, "para" for parallelogram, "
wire_style = "strip"; // "strip" to make space to run nickel strips between cells. Default usage
// "bus" to make space for bus wires between rows
box_style = "both"; // "bolt" for bolting the box pack together
box_style = "bolt"; // "bolt" for bolting the box pack together
// "ziptie" for using zipties to fasten the box together. (ziptie heads will stick out),
// "both" default: uses bolts for the 4 corners and zipties inbetween. Useful for mounting the pack to something with zipties but while still using bolts to hold it together
part_type = "normal"; // "normal","mirrored", or "both". "assembled" is used for debugging. You'll want a mirrored piece if the tops and bottom are different ( ie. When there are even rows in rectangular style or any number of rows in parallelogram. The Console will tell you if you need a mirrored piece).
part = "box bottom"; // "holder" to generate cell holders,
part = "holder"; // "holder" to generate cell holders,
// "cap" to generate pack end caps,
// "box lid" to generate box lid
// "box bottom" for box bottom
// "wire clamp" for strain relief clamp
// "insulator" for insulator piece to fit over the nickel strips
// "vertical box section" for vertical battery stacking boxes (print 1 section for every additional stacked pack)
// "busbar template" to generate a flat template/drawing of the exact cell positions for making nickel strips / bus bars
box_lip = true; // Adds a lip to the box pieces. default = true.
wire_clamp_add = true; // Adds a wire exit hole out the side of the box lid.
insulator_as_support = true; // Print the insulator as a part of the holder support material.
cap_wall = 1.2; // Cap wall thickness (default = 1.2 recommend to make a multiple of nozzle dia)
cap_wall = 0.8; // Cap wall thickness (default = 1.2 recommend to make a multiple of nozzle dia)
cap_clearance = 0.2; // Clearance between holder and caps default = 0.2
box_wall = 2; // Box wall thickness (default = 2.0 recommend to make at least 4 * multiple of nozzle dia)
box_wall = 1.2; // Box wall thickness (default = 2.0 recommend to make at least 4 * multiple of nozzle dia)
box_clearance = 0.2; // Clearance between holder and box default = 0.2
// Box clearances for wires
bms_clearance = 10; // Vertical space for the battery management system (bms) on top of holders, set to 0 for no extra space
bms_clearance = 0; // Vertical space for the battery management system (bms) on top of holders, set to 0 for no extra space
box_bottom_clearance = 0; // Vertical space for wires on bottom of box
box_wire_side_clearance = 3; // Horizontal space from right side (side with wire hole opening) to the box wall for wires
box_wire_side_clearance = 15; // Horizontal space from right side (side with wire hole opening) to the box wall for wires
box_nonwire_side_clearance = 0; // Horizontal space from left side (opposite of wire hole) to the box wall for wires
support_z_gap = 0.3; // Insulator gap to holder. default 0.3
insulator_tolerance = 1.5; // How much smaller to make the width of the insulator default 1.5
insulator_tolerance = 0.4; // How much smaller to make the width of the insulator default 1.5
insulator_thickness = (slot_height-support_z_gap); // Thickness of insulator
wire_diameter = 5; // Diameter of 1 power wire used in the strain relief clamps default = 5 for 10 awg stranded silicon wire
@@ -106,7 +107,7 @@ num_pack_stacks = 1; // How many additional packs you will stack vertically. Aff
cell_top_overlap = 3; // How big the opening overlaps the cell default = 3
opening_dia = cell_dia-cell_top_overlap*2; // Circular opening to expose cell
separation = 1; // Separation between cell top and wire slots (aka tab thickness) default = 1
separation = 0.6; // Separation between cell top and wire slots (aka tab thickness) default = 1
wire_hole_width = 15; // Width of wire hole default = 15
wire_hole_length = 10; // Length of the wireclamp that sticks out default = 10
wire_top_wall = 4; // Thickness of top wire wall default = 4mm
@@ -120,6 +121,23 @@ spacer_overhang = box_clearance + 3; // Amount of spacer overhang to hold the
flip_holders = false; // Mostly used for taking pngs
///////////////////////////////////////////////////////////////////////////////////
// BUSBAR / WELDING TEMPLATE OPTIONS (used when part = "busbar template")
///////////////////////////////////////////////////////////////////////////////////
template_2d = false; // false = printable 3D plate; true = flat 2D drawing for DXF/SVG export (F6 -> Export as DXF / SVG)
template_outline = "rect"; // "rect" = rectangular plate (easy to cut into strips); "hull" = follows the pack outline
template_thickness = 1.5; // Plate thickness for the 3D version
template_margin = 3; // Extra material added around the outermost cells
template_mark_dia = cell_dia; // Diameter of the marked circle at each cell. Set to opening_dia to mark only the exposed weld window
template_hole_style = "engrave";// "engrave" = ring scribed into the top, plate stays solid (best for tracing/marking)
// "through" = holes all the way through (use as a drill / spray-mark jig)
// "center" = only a tiny center hole at each cell (precise punch/drill centers)
template_line_width = 0.8; // Width of the engraved ring line (make a multiple of nozzle dia)
template_engrave_depth = 0.6; // Depth of the engraved ring for "engrave" style
template_center_mark = true; // With "engrave": also add a tiny center hole at each cell for punching exact centers
template_center_mark_dia = 1; // Diameter of the center mark hole
// cell_tab_width = 5; // Width of tab that keeps the cell in the holder default = 5
// cell_tab_length = 3; // Approx Length of tab that keeps the cell in the holder default = 3
@@ -388,6 +406,11 @@ wire_clamp_nib_dia = 5;
{
vertical_box_section(num_pack_stacks);
}
else if(part == "busbar template")
{
busbar_template();
echo_cell_coordinates();
}
else if(part == "flipped holder png")
{
rotate([0,180,0])
@@ -1495,3 +1518,119 @@ function get_pin_list_rect(num_rows,num_cols)
]
];
///////////////////////////////////////////////////////////////////////////
// BUSBAR / WELDING TEMPLATE
// Generates a flat plate (or 2D drawing) with the exact cell positions so it
// can be printed, cut, and used as a template for nickel strips / bus bars,
// or exported as a dimensioned drawing (DXF/SVG).
///////////////////////////////////////////////////////////////////////////
// returns the list of all hex/cell center points for the current pack_style
function get_all_hex_centers()
= pack_style == "rect" ? get_hex_center_points_rect(num_rows,num_cols)
: pack_style == "para" ? get_hex_center_points_para(num_rows,num_cols)
: get_hex_center_points_tria(num_rows,num_cols);
// Echoes every cell center coordinate plus key dimensions to the console.
module echo_cell_coordinates()
{
centers = get_all_hex_centers();
echo("===== CELL CENTER COORDINATES (mm) =====");
echo(str("pack_style = ", pack_style, ", rows = ", num_rows, ", cols = ", num_cols, ", total cells = ", len(centers)));
echo(str("Origin (0,0) = center of first cell. cell_dia = ", cell_dia, ", opening_dia = ", opening_dia));
echo(str("Pitch X (same row, center-to-center) = ", hex_w));
echo(str("Row offset X (between rows) = ", hex_w/2));
echo(str("Pitch Y (row to row, center-to-center)= ", hex_pt*1.5));
for(i = [0:len(centers)-1])
echo(str("Cell ", i+1, ": x = ", centers[i].x, " , y = ", centers[i].y));
echo("========================================");
}
// Small flat ring used for engraving cell outlines
module template_ring(outer_d, line_width, h)
{
difference()
{
cylinder(d = outer_d, h = h);
translate([0,0,-extra])
cylinder(d = max(outer_d - 2*line_width, 0.1), h = h + 2*extra);
}
}
// 2D footprint of the template plate
module template_plate_shape(centers)
{
if(template_outline == "hull")
{
hull()
for(p = centers)
translate([p.x,p.y])
offset(r = template_margin)
polygon([for(a=[0:5])[hex_pt*sin(a*60),hex_pt*cos(a*60)]]);
}
else // rectangular bounding plate
{
xs = [for(p = centers) p.x];
ys = [for(p = centers) p.y];
min_x = min(xs) - cell_radius - template_margin;
max_x = max(xs) + cell_radius + template_margin;
min_y = min(ys) - cell_radius - template_margin;
max_y = max(ys) + cell_radius + template_margin;
translate([min_x, min_y])
square([max_x - min_x, max_y - min_y]);
}
}
// Flat 2D drawing (render with F6, then File -> Export -> Export as DXF / SVG)
module busbar_template_2d(centers)
{
difference()
{
template_plate_shape(centers);
for(p = centers)
translate([p.x,p.y])
circle(d = template_mark_dia);
}
}
// Printable 3D plate
module busbar_template_3d(centers)
{
difference()
{
// Plate
linear_extrude(height = template_thickness)
template_plate_shape(centers);
// Per-cell markings
for(p = centers)
translate([p.x,p.y,0])
{
if(template_hole_style == "through")
translate([0,0,-extra])
cylinder(d = template_mark_dia, h = template_thickness + 2*extra);
else if(template_hole_style == "engrave")
translate([0,0,template_thickness - template_engrave_depth])
template_ring(template_mark_dia, template_line_width, template_engrave_depth + extra);
else if(template_hole_style == "center")
translate([0,0,-extra])
cylinder(d = template_center_mark_dia, h = template_thickness + 2*extra);
// Optional center mark (only useful when material remains, i.e. "engrave")
if(template_center_mark && template_hole_style == "engrave")
translate([0,0,-extra])
cylinder(d = template_center_mark_dia, h = template_thickness + 2*extra);
}
}
}
module busbar_template()
{
centers = get_all_hex_centers();
if(template_2d)
busbar_template_2d(centers);
else
busbar_template_3d(centers);
}