diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8837fe9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +.DS_Store + + +docgen + +*.swp diff --git a/.openscad_docsgen_rc b/.openscad_docsgen_rc new file mode 100644 index 0000000..bb17041 --- /dev/null +++ b/.openscad_docsgen_rc @@ -0,0 +1,4 @@ +DocsDirectory: doc/ +TargetProfile: githubwiki +ProjectName: Battery Holder +GenerateDocs: Files, TOC, Index, Topics, CheatSheet, Sidebar diff --git a/Readme.md b/Readme.md index 3de1bed..ffca2d5 100644 --- a/Readme.md +++ b/Readme.md @@ -1,5 +1,18 @@ # Battery Holder +Here's the documentation: + + +
+ +```include doc/_Sidebar.md``` +
+ +- [Battery Holder](doc/battery-holder.scad.md) +- [Table of Contents](doc/TOC.md) +- [Alphabetical Index](doc/AlphaIndex.md) +- [Topics](doc/Topics.md) + ## Connection Use 4mm nickel strip available at Aliexpress [Nickel Plated Steel Strip Connector](https://www.aliexpress.com/item/1005005127569726.html) R$9.12 (0.1x4mm (50pcs)) @@ -12,4 +25,11 @@ Use 4mm nickel strip available at Aliexpress [Nickel Plated Steel Strip Connecto +## Generate Doc + +```bash +openscad-docsgen -m *.scad +``` + + diff --git a/battery-holder.json b/battery-holder.json new file mode 100644 index 0000000..d30e6b8 --- /dev/null +++ b/battery-holder.json @@ -0,0 +1,6 @@ +{ + "fileFormatVersion": "1", + "parameterSets": { + "New set 1": "" + } +} diff --git a/battery-holder.scad b/battery-holder.scad new file mode 100644 index 0000000..6164115 --- /dev/null +++ b/battery-holder.scad @@ -0,0 +1,648 @@ + +////////////////////////////////////////////////////////////////////// +// LibFile: battery-holder.scad +// Functions and modules for creating battery holders +// Includes: +// include +// FileGroup: Batteries +// FileSummary: This file generates battery holders for arbitrary cylindrical sizes.. +////////////////////////////////////////////////////////////////////// + + +/* [Battery Holder specs] */ + +// Battery Type +battery_type="AAA"; // [None,AA,AAA,18650*,18650P,C,D,CR123A] + +// Number of battery slots to generate +battery_slots = 1; + + + +strip_holes = true; +wire_channels = true; + +battery_symbol = true; +poles_symbol = true; + +contact_bulges = true; + + +// Section: Batteries + + + +////////////////////////////////////////////////////////////////////////////////////// +/// +/// FLEXBATTER: Flexing battery holder with built-in spring +/// +/// This file generates battery holders for arbitrary cylindrical sizes. +/// The number of batteries and their size is fully parametrized. +/// +/// The usual metallic spring at the minus pole is replaced by a +/// flexible printed spring, which is pressing the contacts firmly to +/// the battery. +/// +/// The contacts for the plus and minus pole can easily be made by +/// a few windings of the uninsulated ends of the connecting wires. +/// Each battery compartment contains are several holes to the outside +/// as well ad to the neighboring compartments, through which connecting +/// wires can be passed for easy configuring of parallel, serial or +/// balanced-serial battery packs. +/// +/// The preconfigured battery sizes are: +/// AA, AAA, C, D, 18650(Li-Ion), 18650P(protected Li-Ion), CR123A(16340) +/// +/// Given that the printed plastic spring needs to be flexible, ABS is the material +/// of choice here. +/// +////////////////////////////////////////////////////////////////////////////////////// +/// +/// 2014-09-09 Heinz Spiess, Switzerland +/// +/// released under Creative Commons - Attribution - Share Alike licence (CC BY-SA) +////////////////////////////////////////////////////////////////////////////////////// + + + + + + +/** + * Generates a flexible battery holder for 'n' cylindrical batteries with complex + * features like wire channels, screw holes, and contact points. + * + * @param {number} [n=1] - Number of battery slots to generate. + * @param {number} [l=65] - Length of each battery slot. + * @param {number} [d=18] - Diameter of the battery. + * @param {number} [hf=0.75] - Height factor for the battery holder relative to the battery diameter. + * @param {number} [r=4] - Radius for the plastic spring (calculated inside). + * @param {number} [shd=3] - Screw hole diameter. + * @param {number} [eps=0.28] - Epsilon for additional cavity space around the battery. + * @param {number} [el=0] - Extra length for the spring. + * @param {array} [xchan=[1/4,3/4]] - Relative positions for traversal wire channels. + * @param {number} [$fn=24] - Number of fragments for circles/cylinders for smoother curves. + * + * @description + * This module constructs a battery holder with: + * - A main body with a cavity for each battery. + * - Plastic springs for negative pole contact using the `sline` module. + * - Contact points (+ and -) for electrical connectivity. + * - Wire channels for routing electrical connections. + * - Screw holes for mounting. + * - Engraving for battery orientation symbols. + * + * Key Features: + * - **Chamfering** of edges for a more finished look (`chamfered_cube`). + * - **Deepening** for better grip on batteries (optional). + * - **Wire Channels**: Both longitudinal and transversal for wire routing. + * - **Spring**: Utilizes `sline` to create a complex spring shape for battery contact. + * - **Contact Points**: Bulges for ensuring good electrical contact. + * - **Engraving**: Symbols to indicate battery polarity. + * + * Note: + * - The module uses recursion implicitly through the `for` loop to create multiple slots. + * - Adjustments for middle separators are commented out but could be uncommented for use. + * - Some parameters like `r` are calculated within the module to adjust based on input. + */ + +// Module: battery_case() +// Usage: +// battery_case(n); +// Description: +// Generates a flexible battery holder for 'n' cylindrical batteries with complex +// features like wire channels, screw holes, and contact points. +// +// DefineHeader(BulletList): Parts +// Parts: This module constructs a battery holder with +// A main body with a cavity for each battery. +// Plastic springs for negative pole contact using the `sline` module. +// Contact points (+ and -) for electrical connectivity. +// Wire channels for routing electrical connections. +// Screw holes for mounting. +// Engraving for battery orientation symbols. +// +// DefineHeader(BulletList): Key Features +// Key Features: +// **Chamfering** of edges for a more finished look (`chamfered_cube`). +// **Deepening** for better grip on batteries (optional). +// **Wire Channels**: Both longitudinal and transversal for wire routing. +// **Spring**: Utilizes `sline` to create a complex spring shape for battery contact. +// **Contact Points**: Bulges for ensuring good electrical contact. +// **Engraving**: Symbols to indicate battery polarity. +// +// Arguments: +// n = Number of battery slots to generate. (default: 1) +// l = Length of each battery slot. (default: 65) +// d = Diameter of the battery. (default: 18) +// hf = Height factor for the battery holder relative to the battery diameter. (default: 0.75) +// r = Radius for the plastic spring (calculated inside). (default: 4) +// shd = Screw hole diameter. (default: 3) +// eps = Epsilon for additional cavity space around the battery. (default: 0.28) +// el = Extra length for the spring. (default: 0) +// xchan = Relative positions for traversal wire channels. (default: [1/4, 3/4]) +// $fn = Number of fragments for circles/cylinders for smoother curves. (default: 24) +// +// Example: Simple Case +// battery_case(n=1,l=46.1,d=10.45,hf=0.84,shd=2,el=1,xchan=[0.5],eps=0); + +module battery_case(n=1,l=65,d=18,hf=0.75,r=4,shd=3,eps=0.28,el=0,xchan=[1/4,3/4],$fn=24){ + ew=0.56; // extrusion width + eh=0.25; // extrusion height + w = 4*ew; // case wall thickness + ws = 2*ew; // spring wall thickness + ch = w-ws; // edge chamfering + deepen=0; //deepening for side grip of batteries + //el = 0; // extra length in spring + //eps = 0.28; + //shd = 3; // screw hole diameter + //hf=0.75; + //xchan=[1/4,3/4]; // relative position of traversal wire channels + + r = d/5+2*ws; // linear spring length (depends on sline() call!) + + for(i=[0:n-1])translate([0,i*(d+w+ws),0]){ // generate n battery cases + difference(){ + + union(){ + difference(){ + // main body + translate([0,-w-d/2,0]) + chamfered_cube([l+w,d+2*w+(i0)translate([-1,0,d/2+w])rotate([0,90,0])cylinder(r=d/2+eps,h=l+1); + } + + // plastic spring for minus pole + for(sy=[-1,1])scale([1,sy,1]) //assign(D=d+2*w-2*ws-0.7) + { + D=d+2*w-2*ws-0.7; + translate([ch,d/2+w-ws/2,0])rotate(-90) + //sline([90,0,120,90,120,90,0],[d/8+2,d/6,d/8-1,d/8,-d/8,d/8,d/2],0,ws,hf*d+w); + sline([0,180,0,180,0,-180,0,90,0],[r+ch+el,D/4,el,D/12,el/2,D/12,1+el/2,D/5,D/3],0,ws,hf*d+w); + + + } + } + + // lower and upper holes for contacts + if (strip_holes) + stripHoles(case_wall_thickness = w,spring_wall_thickness=ws,battery_diam = d,slot_length=l); + /* + for(z=[-2*ws,2*ws]) + translate([-2*ws,-w,w-ws/2+d/2+z]) + cube([l+2*w+2,2*w,ws]); + */ + + + // Wire channels + if (wire_channels) + wireChannels( + case_wall_thickness = w, + spring_wall_thickness = ws, + slot_length = l, + battery_diam = d, + spring_radius = r, + channel_pos = xchan, + extrusion_height = eh + ); + + + // grip deepening + if (deepen>0) + translate([w+l/2-0.5,-d/2-w-0.01,w+d+l]) + rotate([-90,0,0]){ + cylinder(r=l+deepen*d,h=d+2*w+2*ws+2,$fn=72); + if(i==0)cylinder(r1=l+deepen*d+ch,r2=l+deepen*d,h=ch+0.02,$fn=72); + if(i==n-1)translate([0,0,d+2*w-ch])cylinder(r2=l+deepen*d+ch,r1=l+deepen*d,h=ch+0.02,$fn=72); + + } + // conical screw holes in corners + for(x=[7+shd,l-2*shd])for(y=[-d/2+shd,d/2-shd]) + translate([x,y,-1]){ + cylinder(r=shd/2,h=w+2); + translate([0,0,w-shd/2+1])cylinder(r1=shd/2,r2=shd,h=shd/2+0.01); + } + + // holes for wires passing inside + for(sy=[-1,1])scale([1,sy,1]){ + translate([l-1,-d/2,w])cube([w+2,2,2]); + for(x=[3,l-7])translate([x,-d/2-w-ws-1,w])cube([3,w+ws+3,2]); + translate([3,-d/2+w/2-0.75,-1])cube([3,1.5,w+2]); + translate([-0.5,-d/2+w/2,0])rotate([0,90,0])cylinder(r=w/2,h=6.5,$fn=5); + } + + + if (battery_symbol) + batterySymbol( + case_wall_thickness = w, + battery_diam = d, + slot_length = l, + extrusion_height = eh, + ); + + // engrave plus symbol + //assign(sy=(l>12*shd)?1:-1) + if (poles_symbol) + polesSymbol( + case_wall_thickness = w, + battery_diam = d, + slot_length = l, + screw_hole_diameter = shd, + extrusion_height = eh, + + ); + + + + + if (contact_bulges) + contactBulges( + case_wall_thickness = w, + spring_wall_thickness = ws, + battery_diam = d, + slot_length = l, + spring_extra_length = el, + vertical_bulge = true + + + ); + + } +} + +module contactBulges( case_wall_thickness, spring_wall_thickness, battery_diam,slot_length, spring_extra_length, vertical_bulge ) { + // horizontal contact bulges (+ and - pole) + for( x = [ -0.3,slot_length ] ) + hull() + for(y=[ + -3+spring_extra_length, + +3-spring_extra_length + ]) + translate([ + x, + y, + case_wall_thickness + battery_diam / 2 + ]) + sphere(r=spring_wall_thickness); + + // vertical contact bulge (+ pole only) + + if (vertical_bulge) hull() + for(z=[-3+spring_extra_length,3-spring_extra_length]) + for(x=[0,case_wall_thickness-spring_wall_thickness]) + translate([ slot_length + x,0,case_wall_thickness + battery_diam / 2 + z]) + sphere(r=spring_wall_thickness); +} + + +/** + * Engraves a simple battery symbol on the side of a battery case. + * + * @param {number} case_wall_thickness - Thickness of the case walls. + * @param {number} slot_length - Length of the battery slot. + * @param {number} battery_diam - Diameter of the battery, used for symbol size and positioning. + * @param {number} extrusion_height - Height of the extrusion for the symbol. + * + * @description + * This module creates a visual representation of a battery by engraving: + * - A rectangle representing the battery body. + * - A smaller rectangle at one end to symbolize the positive terminal or battery cap. + * + * The positioning of the symbol is: + * - Centered along the length of the slot with an offset for the case wall thickness. + * - Placed slightly above the bottom of the battery diameter for visibility. + * + * Notes: + * - Both rectangles are centered (`true` in the `cube` calls) for symmetrical engraving. + * - The dimensions of the symbol parts are proportional to `slot_length` and `battery_diam`, ensuring the symbol scales appropriately with different battery sizes. + */ +module batterySymbol( case_wall_thickness, slot_length, battery_diam,extrusion_height ) { + translate( + [ + case_wall_thickness+slot_length/2, + battery_diam/4+1, + case_wall_thickness + ]) + cube( + [ + slot_length/5, + battery_diam/4.5, + 4*extrusion_height + ],true); + translate( + [ + case_wall_thickness+slot_length/2+slot_length/10, + battery_diam/4+1, + case_wall_thickness + ]) + cube([battery_diam/7,battery_diam/10,4*extrusion_height],true); +}; + + +/** + * Creates strip-like holes on both ends of the battery slot for contact purposes. + * + * @param {number} case_wall_thickness - Thickness of the case walls. + * @param {number} spring_wall_thickness - Thickness of the spring or contact wall. + * @param {number} battery_diam - Diameter of the battery, used for positioning. + * @param {number} slot_length - Length of the battery slot. + * + * @description + * This module generates two rectangular holes at each end of the battery compartment: + * - These holes are designed to facilitate contact between the battery terminals and external connectors or springs. + * - They are positioned at the top and bottom of the battery diameter area, relative to the case's interior. + * + * Key features: + * - **Positioning**: Holes are placed at the ends of the battery slot, with one at `z = -2*spring_wall_thickness` and another at `z = 2*spring_wall_thickness` above and below the battery's center line. + * - **Dimensions**: + * - Width spans the length of the slot plus additional space for case walls. + * - Height is twice the case wall thickness, ensuring enough room for contact. + * - Depth (or height in OpenSCAD's coordinate system) is equal to the spring wall thickness. + * + * Note: + * - The module uses a loop to create both holes symmetrically around the battery's center. + * - The exact positioning ensures these holes do not interfere with the battery's body but are accessible for contacts. + */ +module stripHoles( case_wall_thickness,spring_wall_thickness,battery_diam,slot_length ) { + for( z = [-2*spring_wall_thickness,2*spring_wall_thickness] ) + translate([ + -2*spring_wall_thickness, + -case_wall_thickness, + case_wall_thickness-spring_wall_thickness/2+battery_diam/2+z + ]) + cube([ + slot_length+2*case_wall_thickness+2, + 2*case_wall_thickness, + spring_wall_thickness + ]); +} + + +/** + * Creates wire channels in the battery case to route electrical connections. + * + * @param {number} case_wall_thickness - Thickness of the case walls. + * @param {number} spring_wall_thickness - Thickness of the spring or contact wall. + * @param {number} slot_length - Length of the battery slot. + * @param {number} battery_diam - Diameter of the battery. + * @param {number} spring_radius - Radius of the spring or additional space for wiring. + * @param {array} channel_pos - Array of relative positions for transversal channels along the slot's length. + * @param {number} extrusion_height - Height of the extrusion for positioning the wire channel. + * + * @description + * This module constructs wire channels for guiding wires: + * - **Longitudinal Channel**: Runs along the bottom of the battery case for wires connecting multiple batteries or exiting the case. + * - **Transversal Channels**: Provides paths for wires to cross the width of the case, positioned according to `channel_pos`. + * + * Key Features: + * - **Longitudinal Channel**: + * - Positioned at the base of the case, extending the length of the slot plus additional space. + * - Rotated to lie horizontally under the battery for ease of wire routing. + * - **Transversal Channels**: + * - Created at specified positions along the slot's length (`channel_pos`). + * - Run perpendicular to the longitudinal channel, allowing for cross-connection of wires or external connections. + * + * Notes: + * - Cylinders are used to form these channels, with $fn determining the number of facets for a smoother or more angular appearance. + * - The longitudinal channel's height includes the slot length plus extra space for routing flexibility. + * - Transversal channels are placed just below the battery's diameter to avoid interfering with battery placement. + */ +module wireChannels( case_wall_thickness, spring_wall_thickness,slot_length, battery_diam, spring_radius, channel_pos,extrusion_height ) { + // longitudinal bottom wire channel + translate([-2*spring_wall_thickness,0,0]) + rotate([0,90,0]) + cylinder( + r=case_wall_thickness/2, + h=slot_length + case_wall_thickness + 2 +spring_radius, + $fn=5 + ); + + // traversal bottom wire channels + for ( x = slot_length * channel_pos ) + translate([x,-battery_diam/2-case_wall_thickness-1,extrusion_height]) + rotate([-90,0,0]) + cylinder( + r = case_wall_thickness/2, + h = battery_diam +2 * case_wall_thickness + spring_wall_thickness+2, + $fn=6 + ); + +} + + +/** + * Engraves symbols for battery polarity (+ and -) on the side of the battery case. + * + * @param {number} case_wall_thickness - Thickness of the case walls. + * @param {number} slot_length - Length of the battery slot. + * @param {number} screw_hole_diameter - Diameter of the screw holes, used to determine symbol orientation. + * @param {number} battery_diam - Diameter of the battery, used for symbol size and positioning. + * @param {number} extrusion_height - Height of the extrusion for the symbols. + * + * @description + * This module adds visual indicators for battery polarity: + * - For longer batteries, symbols are placed horizontally. + * - For shorter batteries, symbols are adjusted vertically to fit within the available space. + * + * The symbols are: + * - **Plus (+)**: Created by intersecting two cubes to form a cross. + * - **Minus (-)**: A single cube representing a line. + * + * The placement and orientation of these symbols depend on: + * - `slot_length` compared to `screw_hole_diameter` to determine if the battery is considered 'short' or 'long'. + * - `sy` (scale y) determines whether symbols are placed on the top or side based on the comparison. + * + * Note: + * - The `echo` statement is included for debugging or informational purposes, to verify the battery diameter. + * - The commented-out code for middle separators suggests this module might have been part of a larger context where such adjustments were necessary but have since been removed or commented out. + */ +module polesSymbol( case_wall_thickness, slot_length,screw_hole_diameter, battery_diam, extrusion_height ) { + sy=( slot_length > 12 * screw_hole_diameter)?1:-1; + echo ("battery_diam",battery_diam); + { // for short batteries +- on the side + translate([ + case_wall_thickness+slot_length/2+slot_length/(sy>0?5:10), + sy*(battery_diam/4+1), + case_wall_thickness + ]){ + cube([1,battery_diam/4,4*extrusion_height],true); + cube([battery_diam/4,1,4*extrusion_height],true); + } + // engrave minus symbol + translate([ + case_wall_thickness+slot_length/2-slot_length/(sy>0?5:10), + sy*(battery_diam/4+1), + case_wall_thickness + ]) + cube([1,battery_diam/4,4*extrusion_height],true); + } + + //correction for middle separators + //if(i0) translate([-d,-d/2-w-0.1,-1])cube([d,ws/2+0.1,d+2]); + //else translate([1,-d/2-w,-0.01])cylinder(r1=ch,r2=0,h=ch); + } +} + + + + +//caseCR123A(n=2); +if (battery_type == "AAA") { + battery_case(n=battery_slots,l=46.1,d=10.45,hf=0.84,shd=2,el=1,xchan=[0.5],eps=0); +} else if ( battery_type == "AA" ) { + battery_case(n=battery_slots,l=51.6,d=14.4,hf=0.80,shd=2.5,el=0.5,eps=0.28); +} else if ( battery_type == "C" ) { + battery_case(n=battery_slots,l=51.6,d=26.4,hf=0.75,shd=3,eps=0.28); +} else if ( battery_type == "D" ) { + battery_case(n=battery_slots,l=61.5,d=34.0,hf=0.75,shd=3,eps=0.28); +} else if ( battery_type == "18650*" ) { + battery_case(n=battery_slots,l=65.5,d=18.4,hf=0.75,shd=3,eps=0.28); +} else if ( battery_type == "18650P") { + battery_case(n=battery_slots,l=67.5,d=18.4,hf=0.75,shd=3,eps=0.28); +} else if ( battery_type == "CR123A") { + battery_case(n=battery_slots,l=35.1,d=16.7,hf=0.75,shd=3,xchan=[0.5],eps=0.28); +} else { + echo (str("ERROR for battery type :'",battery_type,"'")); +} + + + + + +/** + * Creates an arc or a segment of a cylinder with inner and outer radii. + * + * @param {number} r1 - Inner radius of the arc. + * @param {number} r2 - Outer radius of the arc. + * @param {number} height - The height of the arc segment. + * @param {number} [a1=0] - Starting angle of the arc in degrees. + * 0 degrees is along the positive X-axis. + * @param {number} [a2=0] - Ending angle of the arc in degrees. + * Positive values are counter-clockwise from a1. + * + * @description + * This module constructs a 3D arc segment between two radii (`r1` and `r2`) + * from `a1` to `a2` degrees. The arc's height is determined by `height`. + * If `a2 - a1` is less than or equal to 180 degrees, it uses a direct + * difference operation to carve out the arc, otherwise, it uses intersection + * for arcs greater than 180 degrees to ensure proper geometry. + * + * The function uses cylinders and cubes for creating and subtracting shapes: + * - A larger cylinder (`r2`) forms the outer arc. + * - A smaller cylinder (`r1`) is subtracted to make the inner void. + * - Cubes are positioned and rotated to cut away the unwanted parts of the + * cylinders, forming the arc or segment shape. + * + * Note: + * - All rotations are in the XY plane (around the Z-axis). + * - The height parameter includes an extra 2 units for the difference and + * intersection operations to ensure clean cuts through the cylinders. + */ +module arc( r1, r2, height, a1=0, a2=0 ) { + if (a2 - a1 <= 180) { + // For arcs less than or equal to 180 degrees, we can directly cut the shape + difference() { + // Create the outer cylinder + cylinder(r=r2, h=height); + // Remove the inner part to make it hollow + translate([0,0,-1]) cylinder(r=r1, h=height+2); + // Cut off one side of the arc + rotate(a2) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]); + // Cut off the other side of the arc + rotate(a1+180) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]); + } + } else { + // For arcs greater than 180 degrees, we use intersection to ensure we keep the correct shape + difference() { + cylinder(r=r2, h=height); // Outer shape + translate([0,0,-1]) cylinder(r=r1, h=height+2); // Inner void + intersection() { + // First cut to carve the arc shape + rotate(a2) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]); + // Second cut for arcs greater than 180 degrees to ensure we keep only the arc + rotate(a1+180) translate([-r1-r2,0,-1]) cube([2*(r1+r2),2*(r1+r2),height+2]); + } + } + } +} + + +/** + * Generates a "snake line" or a path with segments that can turn or go straight. + * + * @param {array} angle - An array of angles for each segment. + * Positive values indicate a left/counter-clockwise turn, + * negative for right/clockwise, and zero for straight segments. + * @param {array} radius - An array of radii for each segment. + * The absolute value of radius[i] is used for turning segments, + * and directly for straight segments. + * @param {number} i - Current index in the angle and radius arrays. + * @param {number} w - Width of the snake line. + * @param {number} h1 - Height of the snake line. + * + * @description + * This module constructs a continuous line that can bend at specified angles + * and lengths. It uses recursion to build the path segment by segment: + * + * - For each segment: + * - If the angle is non-zero (`a != 0`), it creates an arc using the `arc` module, + * where the radii of the arc are adjusted by `w/2` to account for the line's width. + * - For zero angle (`a == 0`), it simply extends straight using a cube. + * - The `scale` operation ensures the correct orientation of the arc for negative angles. + * - The `translate` and `rotate` operations move and orient each new segment relative + * to the last one. + * + * Notes: + * - The module uses a tiny overlap (`-r-0.01`) when placing cubes to ensure a seamless join. + * - Recursive calls to `sline` build the whole path until all angles in the array are processed. + */ +module sline(angle, radius, i, w, h1) { + scale([angle[i] >= 0 ? 1 : -1, 1, 1]) { // Flip for negative angles + // Declare variables for current segment + r = abs(radius[i]); // Radius for current segment + a = angle[i]; // Angle for current segment + + translate([a ? r : 0, 0, 0]) { // Move to start of segment + translate([-w/2, -r - 0.01, 0]) // Position for overlap + cube([w, 0.02, h1]); // Small cube for overlap + + if (a) { + // Create an arc if there's an angle (turn) + arc( + r1 = r - w/2, // Inner radius of arc + r2 = r + w/2, // Outer radius of arc + a1 = a, // Start angle, adjusted to be from 0 to a + height = h1 // Height of the arc + ); + } else if (r > 0) { + // Create straight segment if no turn and radius is positive + translate([-w/2, -r, 0]) cube([w, r, h1]); + } + + if (i + 1 < len(angle)) { // Check if there's another segment to process + rotate(angle[i]) // Rotate for the next segment + translate([a ? -r : 0, a ? 0 : -r, 0]) // Translate to the end of current segment + sline(angle, radius, i + 1, w, h1); // Recursively call for next segment + } + } + } +} +// build a cube with chamfered edges +module chamfered_cube(size,d=1){ + hull(){ + translate([d,d,0])cube(size-2*[d,d,0]); + translate([0,d,d])cube(size-2*[0,d,d]); + translate([d,0,d])cube(size-2*[d,0,d]); + } +} + + + + diff --git a/doc/.source_hashes b/doc/.source_hashes new file mode 100644 index 0000000..50c8c74 --- /dev/null +++ b/doc/.source_hashes @@ -0,0 +1 @@ +battery-holder.scad|6ed4929e3433db05a34a3565e39c611c992f9c4d586969cd2e0d3cd5bb49db3e diff --git a/doc/AlphaIndex.md b/doc/AlphaIndex.md new file mode 100644 index 0000000..4a75674 --- /dev/null +++ b/doc/AlphaIndex.md @@ -0,0 +1,10 @@ +# Alphabetical Index + +An index of Functions, Modules, and Constants by name. + +[B](#b) + +## B + +- [`battery_case()`](battery-holder.scad#module-battery_case) Mod + diff --git a/doc/TOC.md b/doc/TOC.md new file mode 100644 index 0000000..600050d --- /dev/null +++ b/doc/TOC.md @@ -0,0 +1,16 @@ +# Table of Contents + +## List of Files + +**Batteries:** + +- [battery-holder.scad](#1-battery-holderscad) ([docs](battery-holder.scad)) +This file generates battery holders for arbitrary cylindrical sizes.. + +## 1. [battery-holder.scad](battery-holder.scad) + +This file generates battery holders for arbitrary cylindrical sizes.. +- [Batteries](battery-holder.scad#section-batteries) + - [`battery_case()`](battery-holder.scad#module-battery_case) Mod + + diff --git a/doc/Topics.md b/doc/Topics.md new file mode 100644 index 0000000..3afd7c7 --- /dev/null +++ b/doc/Topics.md @@ -0,0 +1,4 @@ +# Topic Index + +An index of topics, with related functions, modules, and constants. + diff --git a/doc/_Sidebar.md b/doc/_Sidebar.md new file mode 100644 index 0000000..7af479d --- /dev/null +++ b/doc/_Sidebar.md @@ -0,0 +1,10 @@ +[Table of Contents](TOC) +[Function Index](AlphaIndex) +[Topics Index](Topics) + +## List of Files: + +**Batteries:** + +- [battery-holder.scad](battery-holder.scad) + diff --git a/doc/battery-holder.scad.md b/doc/battery-holder.scad.md new file mode 100644 index 0000000..47ecf64 --- /dev/null +++ b/doc/battery-holder.scad.md @@ -0,0 +1,72 @@ +# LibFile: battery-holder.scad + +Functions and modules for creating battery holders + +To use, add the following lines to the beginning of your file: + + include + +## File Contents + +1. [Section: Batteries](#section-batteries) + - [`battery_case()`](#module-battery_case) + + +## Section: Batteries + + +### Module: battery\_case() + +**Usage:** + +- battery_case(n); + +**Description:** + +Generates a flexible battery holder for 'n' cylindrical batteries with complex + features like wire channels, screw holes, and contact points. + +**Parts:** This module constructs a battery holder with + +- A main body with a cavity for each battery. +- Plastic springs for negative pole contact using the `sline` module. +- Contact points (+ and -) for electrical connectivity. +- Wire channels for routing electrical connections. +- Screw holes for mounting. +- Engraving for battery orientation symbols. + +**Key Features:** + +- **Chamfering** of edges for a more finished look (`chamfered_cube`). +- **Deepening** for better grip on batteries (optional). +- **Wire Channels**: Both longitudinal and transversal for wire routing. +- **Spring**: Utilizes `sline` to create a complex spring shape for battery contact. +- **Contact Points**: Bulges for ensuring good electrical contact. +- **Engraving**: Symbols to indicate battery polarity. + +**Arguments:** + +By Position | What it does +-------------------- | ------------ +`n` | Number of battery slots to generate. (default: 1) +`l` | Length of each battery slot. (default: 65) +`d` | Diameter of the battery. (default: 18) +`hf` | Height factor for the battery holder relative to the battery diameter. (default: 0.75) +`r` | Radius for the plastic spring (calculated inside). (default: 4) +`shd` | Screw hole diameter. (default: 3) +`eps` | Epsilon for additional cavity space around the battery. (default: 0.28) +`el` | Extra length for the spring. (default: 0) +`xchan` | Relative positions for traversal wire channels. (default: [1/4, 3/4]) +`$fn` | Number of fragments for circles/cylinders for smoother curves. (default: 24) + +**Example 1:** Simple Case + +battery\_case() Example 1 + +
+ + include + battery_case(n=1,l=46.1,d=10.45,hf=0.84,shd=2,el=1,xchan=[0.5],eps=0); + +--- + diff --git a/doc/images/battery-holder/battery_case.png b/doc/images/battery-holder/battery_case.png new file mode 100644 index 0000000..775a926 Binary files /dev/null and b/doc/images/battery-holder/battery_case.png differ diff --git a/doc/images/battery-holder/case.png b/doc/images/battery-holder/case.png new file mode 100644 index 0000000..775a926 Binary files /dev/null and b/doc/images/battery-holder/case.png differ diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..76590a8 --- /dev/null +++ b/run.sh @@ -0,0 +1,61 @@ +#!/bin/bash + +# Function to install docgen +install_docgen() { + echo "Creating virtual environment..." + python3 -m venv docgen + source docgen/bin/activate + echo "Installing openscad_docsgen..." + pip install openscad_docsgen + echo "Installation completed." +} + +# Function to run openscad-docsgen with your specified options +run_docsgen() { + # Check if we are in the virtual environment + if [ -z "$VIRTUAL_ENV" ] || [ ! "$(basename "$VIRTUAL_ENV")" == "docgen" ]; then + echo "Activating docgen virtual environment..." + source docgen/bin/activate || { echo "Failed to activate virtual environment. Make sure 'docgen' exists."; return 1; } + fi + echo "Running openscad-docsgen..." + openscad-docsgen -m -I -P "Metalib" -f -p wiki -i -t -m -s + echo "Documentation generation completed." +} + +# Function to uninstall (deactivate the virtual environment) +uninstall_docgen() { + echo "Deactivating virtual environment..." + deactivate + echo "Virtual environment deactivated." +} + +# Main script logic +echo "OpenSCAD DocsGen Script" +echo "----------------------" +echo "1. Doc Generation" +echo "2. Install docgen" +echo "3. Uninstall docgen" +echo "q. Exit" + +while true; do + read -p "Choose an option (1/2/3/q): " choice + + case $choice in + 1) + run_docsgen + ;; + 2) + install_docgen + ;; + 3) + uninstall_docgen + ;; + q) + echo "Exiting script. Goodbye!" + exit 0 + ;; + *) + echo "Invalid option. Please try again." + ;; + esac +done