Improved OpenSCAD battery holder script (BigClive) (WHL #97)
A while back, BigClive used to share OpenSCAD snippets in a lot of his DIY videos, allowing viewers to print (and modify) things that he figured out for the specific video. One of those was the battery holder script, a simple array of square-packed cylinders that were hollowed out from one side, in order to, well, hold things like batteries, or hex bits, or screwdrivers, or …
I recently had the urge to store a bunch of batteries in a nice way and began modifying the simple 33-line script. Couple of viewers already suggested changes like center holes, and since OpenSCAD scripts are just plain text, they were able to share their modifications in the comment section. So I had a go to make it fit for my set of problems and ended up in 80 lines of code [script at the bottom of the page!]. In addition to the stuff Clive explains in his video, this script also accepts (well, requires) these parameters:
- breather: central breather hole, e.g. access for measuring batteries without taking them out, or to drain fluids, or to remove suction effects for very tight fitting objects
- linkwidth: widened links between the cups, to a) allow for easier print (my printer has issues with the acute corners the original packing algorithm produces) and b) stiffen the structure (ideal for larger prints)
The link width set to full outer width creates a smooth outer contour, which can be useful for mounting purposes, cleaner prints, or for joining different arrays together (although I wouldn’t recommend that and instead create a larger array and add removable adapter rings instead). Rings can be made with width=height=1 setting and either zero base width or breather hole size = inner diameter. I’m aware that 80 lines of code is a bit overkill for creating a hollow cylinder, but that’s one edge case the script can handle.
Sizing hints for different battery types are included in the script, these are tight fits and may need adjustments. Be aware that not all batteries are created equal, some do max out the allowable dimensions and then use thick wrappers, while others (China and OEM stuff! Check the center position in row three from the top) can actually be a millimeter thinner than their branded counterparts. Measuring several different types can be very helpful in determining a size that fits all.
The script also contains a makehalfcups() subroutine that slices the cylinders on a different axis. That might not be terribly useful for ordinary AA batteries, but it is for button cells that are difficult to get out from a stack. It’ll probably work as well for (bullion) coins, but your Maple Leafs clearly deserve a fancier holder!
Note that when stacking like this, the batteries are in contact with each other unless insulating material is added in between or multiple halfcups() are merged with their borders separating them. This means that the outer batteries will present a certain voltage, especially when pinched together. This might not be an issue for small amounts of 1.5V cells, but this stack of 14 LiSoCl2 cells (plus one CR-whatever as a filler) is already at ~55V,
and this stack of 20x CR2025/2032 is easily over 60 volts, with short circuit currents of 100mA+. People have died from battery packs far less spicy than that, so don’t be an idiot hoarder here.
The smaller groove in front for the AG13s and other fun-sized batteries is created with the very same script by adjusting outer and inner diameters accordingly, so it fits in the larger original print. Same principle as with the adapter rings for upright arrays of batteries. Currently there’s no way to generate rectangular shapes, so for those 9V blocks inside of an D-cell array, I made cylinders and subtracted a rectangular block in a different script. Since those need to be printed one after another, that’s super easy to do by hand. For arrays of 9V cells…well, feel free to mod the script again.
In the end, I made almost a dozen of these in various sizes. I found it ideal to stick to arrays of around 10cm x 10cm, since that is large enough to not tip over, stack securely, still handy to put somewhere, and many battery sizes meet at very similar dimensions – 9×9 AAA with my wall thickness size has a side length of 107.4mm, 7×7 AA is 111.8mm, 5×5 18650 is 101.2mm, 3×3 D-cell is 104.7mm, and the 4-wide CR2450 tray is 103.4mm. I haven’t yet found a way to lock them together with a print that can lay flat without much support structure, but maybe someone has a bright idea to fix that.
I also made this super-sized version that holds spray cans, something that annoyed me for years. I keep these in an Ikea box that fits nicely into the bottom drawer of a cupboard, but they usually tipped over and it’s a mess, especially with other stuff thrown in on top.
Smaller cans like the WD40 got a ring adapter, as already discussed for batteries – I wasn’t satisfied with the mechanical properties of a dual version that had both sizes integrated into one tray.
Doesn’t have to be a super strong print, these are about 30g a piece, plus the 350g-ish base plate for all of them. And then it fits the Gopan box nicely with minimal wobble. Lovely!
Large holder holds small holder:
Aaand here’s the script:
//Custom battery tray - bigclivedotcom + Bzzz $fn=50; //number of facets - 50 is decent, 100 for large cups makecups(4, 4, 69.0, 30, 1.8, 1, 3, 30); ////makecups has 8 parameters: width, height, diameter, depth, thickness, base, breather, linkwidth //makehalfcups (3, 20.35, 63.45, 1.2); //makehalfcups has 4 parameters: width, diameter, depth, thickness module makehalfcups(width, diameter, depth, thickness){ difference(){ union(){ makecups(1, width, diameter, 0.5*depth, thickness, thickness, 0, diameter + 2*thickness); translate([0, 0, depth+2*thickness]){ rotate([0, 180, 0]){ makecups(1, width, diameter, 0.5*depth, thickness, thickness, 0, diameter + 2*thickness); } } } translate([0, -0.5*diameter-2*thickness, -thickness]){ cube([diameter, width * (diameter + 2*thickness), 2*depth+thickness]); } } } module makecups(width, height, diameter, depth, thickness, base, breather, linkwidth){ //width: number of horizontal cups (x direction) //height: number of vertical cups (y direction) //depth: usable depth of cups //dosen 30, 20 18650+, 15 AA, 12 AAA //diameter: inner diameter of cups //spray cans 69.2 (small ones 54.0) // 26650 26.55, 18650 18.8, 2450 24.3, // D 33.3, AA 14.65, AAA 10.8 //thickness: wall thickness, use integer multiples of nozzle size //base: thickness of cup base, use integer multiples of slicer layer height //breather: diameter of breather hole in center of cup (set to 0 for none) //linkwidth: size of widened structure between cups (set to 0 to use walls only) columns=width-1; rows=height-1; wall=thickness*2; dia=diameter+thickness; radius=diameter/2; rad = radius + thickness; cup=depth+base; difference(){ union(){ for (x=[0:columns]){ //main body for (y=[0:rows]){ translate([x*dia,y*dia,0]){ cylinder(h=cup,d=diameter+wall); } translate([x*dia+rad/2,(y*dia),(depth+base)/2]){ if(x!=columns){cube(size=[rad, linkwidth, cup], center=true);} } translate([x*dia,y*dia+rad/2,(depth+base)/2]){ if(y!=rows){cube(size=[linkwidth, rad, cup], center=true);} } translate([x*dia-rad/2,(y*dia),(depth+base)/2]){ if(x!=0){cube(size=[rad, linkwidth, cup], center=true);} } translate([x*dia,y*dia-rad/2,(depth+base)/2]){ if(y!=0){cube(size=[linkwidth, rad, cup], center=true);} } } } } for (x=[0:columns]){ //hollow cores for (y=[0:rows]){ translate([x*dia,y*dia,base]){ cylinder(h=cup,d=diameter); } translate([x*dia,y*dia,-1]){ cylinder(h=2*cup,d=1*breather); } } } } }
Have fun hoarding batteries!