Stackable Battery Holder
A wall-mounted battery holder that keeps cells standing upright in a row of pockets, with a finger slot in front of each pocket so you can push a battery up and grab it. Modules stack vertically: a rail on top of the back plate fits into a groove under the next module, keeping them aligned, and each module is fixed to the wall with two countersunk screws. The default six-pocket AA module measures 104.6 x 26.1 x 68 mm. Print it upright as exported; no supports are needed. You can choose the battery type (AA, AAA or 18650), the number of pockets (2-12), the pocket clearance and the rail-to-groove fit.
# Stackable Wall Battery Holder - AA / AAA / 18650 pockets with rail-and-groove stacking
#
# One module: a block of battery pockets attached to a back plate (wall side, y=0).
# Batteries stand upright with about 1/4 of their length exposed. Push them up through the front finger slot to remove.
# Stacking: a rail (male) on the top edge of the back plate and a groove (female) on the bottom. The groove of the upper
# module fits over the rail of the lower one, aligning them side to side and front to back. Each module is fixed with two countersunk screws.
# Print as is (upright, back plate standing). The groove opens on the bottom face, so no supports are needed.
@param choices:["AA", "AAA", "18650"] desc:"Battery type"
battery = "AA"
@param 2..12 step:1 desc:"Number of pockets"
slots = 6
@param 0.2..1 step:0.1 desc:"Radial pocket clearance (per side, mm)"
clear = 0.4
@param 0.1..0.4 step:0.05 desc:"Rail-to-groove clearance (per side, mm)"
fit = 0.2
# Battery size (diameter, length). AA/AAA are JIS C 8500 / IEC 60086 maximums; 18650 is a typical unprotected cell
bd = if battery == "AA" then 14.5 else if battery == "AAA" then 10.5 else 18.5
bl = if battery == "AA" then 50.5 else if battery == "AAA" then 44.5 else 65.2
rib = 1.6 # divider between pockets (0.4mm x 4 passes)
side = 2.4 # outer wall
floor_t = 2 # pocket floor
bt = 6 # back plate thickness
pr = bd / 2 + clear # pocket radius
pitch = 2 * pr + rib
depth = round(bl * 0.75) # pocket depth (3/4 of the battery)
width = slots * pitch - rib + 2 * side
block_d = bt + side + 2 * pr + side # depth including the back plate
block_h = depth + floor_t
mod_h = block_h + round(bl * 0.25) + 12 # module height = spacing to the next module
# --- Body ---------------------------------------------------------
plate = box width bt mod_h | floor | translate 0 (bt / 2) 0
block = box width block_d block_h | floor | translate 0 (block_d / 2) 0
| edges =Z >Y | fillet 2
x0 = -(slots - 1) * pitch / 2
yc = bt + side + pr
pockets = union [cylinder pr (depth + 1) | floor
| translate (x0 + i * pitch) yc floor_t for i in range(slots)]
# Finger slots: open the front of each pocket from the top down to half its depth
slot_w = round(bd * 0.5)
slot_h = round(depth * 0.5)
fingers = union [box slot_w (side * 3) (slot_h + 1) | floor
| translate (x0 + i * pitch) (block_d) (block_h - slot_h)
for i in range(slots)]
# --- Stacking (rail / groove) ----------------------------------------
rail_w = 2.4 # rail thickness (Y)
rail_h = 3 # rail height
rail_l = width - 2 * side * 2 # slightly shorter at both ends
rail = box rail_l rail_w rail_h | floor | translate 0 (bt / 2) mod_h
groove = box (rail_l + 2 * fit) (rail_w + 2 * fit) (rail_h + fit) | floor
| translate 0 (bt / 2) -0.01
# --- Countersunk wall screw holes (wood screw 4mm, clearance 4.5mm, head 8.5mm) ------------
screw_z = block_h + (mod_h - block_h) / 2
screw_x = width / 2 - side - 8
csk = union [cylinder 2.25 (bt + 2) | rotate 90 0 0 | translate 0 (bt / 2) 0,
cone 2.25 4.75 2.5 | rotate -90 0 0 | translate 0 (bt - 0.75) 0]
screws = union [csk | translate screw_x 0 screw_z,
csk | translate (-screw_x) 0 screw_z]
union [plate, block, rail]
| diff pockets
| diff fingers
| diff groove
| diff screws