After five years, it’s time to replace the Anker 13000 mA·hr USB power banks / chargers I used with the M20 cameras and then the C100 cameras:

The Anker 325 20K V2 power bank is considerably chunkier, as befits its 20,000 mA·hr cell capacity (although the fine print says 12,500 mA·hr output):

The white tape stripe on the top marks the USB port on the end to reduce the fumbling involved in an out-of-sight socket. There’s also a USB-C port on that end for both charging the pack and powering other devices.
The new mounting cradle descends directly from the 13000 cradle:

The model includes a projection of the battery on the XY plane for export to an SVG file suitable for laser-cutting an EVA foam pad to cushion the bumps.
The OpenSCAD source code as a GitHub Gist:
| // Anker PowerCore 325 20K V2 Power Bank | |
| // Ed Nisley – KE4ZNU | |
| // 2024-07 | |
| /* [Layout Options] */ | |
| Layout = "Show"; // [Show,Build] | |
| Part = "Cradle"; // [Cradle,Battery,Pad] | |
| /* [Extrusion Parameters] */ | |
| ThreadWidth = 0.40; | |
| ThreadThick = 0.25; | |
| HoleWindage = 0.2; | |
| Protrusion = 0.1; | |
| //—– | |
| // Dimensions | |
| /* [Hidden] */ | |
| ID = 0; | |
| OD = 1; | |
| LENGTH = 2; | |
| EmbossDepth = 2*ThreadThick + Protrusion; // recess depth + Protrusion beyond surface | |
| DebossHeight = EmbossDepth; // text height + Protrusion into part | |
| Projection = 10; // stick-out to punch through shell sides & suchlike | |
| FadeColor = "Green"; | |
| FadeAlpha = 0.25; | |
| //—– | |
| // Useful routines | |
| function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit); | |
| module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes | |
| Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2); | |
| FixDia = Dia / cos(180/Sides); | |
| cylinder(r=(FixDia + HoleWindage)/2, | |
| h=Height, | |
| $fn=Sides); | |
| } | |
| //—– | |
| // Battery | |
| // Based on Anker PowerCore 325, simplified shapes | |
| // Includes port punchouts | |
| Battery = [162.0+0.5,81.5+0.5,24.0]; // X=length, Y=max, Z=max | |
| module BatteryShape(Jacks = true) { | |
| USB = [[Projection,20,12],[Projection,18,10]]; // clearance around USB output jacks | |
| USBOffset = [[0,20.0,0],[0,62.5,0]]; // from -Y edge to center of jack, Z centered | |
| BatteryRad = 7.0; // corner rounding radius | |
| BatterySides = 2*3*4; | |
| hull() | |
| for (i=[-1,1], j=[-1,1]) | |
| translate([i*(Battery.x/2 – BatteryRad), | |
| j*(Battery.y/2 – BatteryRad), | |
| 0]) | |
| cylinder(r=BatteryRad,h=Battery.z,$fn=BatterySides,center=true); | |
| if (Jacks) | |
| for (i=[0,len(USB)-1]) | |
| translate([(Battery.x + USB[i].x)/2 – Protrusion,-Battery.y/2 + USBOffset[i].y,0]) | |
| cube(USB[i],center=true); | |
| } | |
| //—– | |
| // Battery cradle | |
| RackWidth = 90.0; // flat width between rack rails | |
| CradleWall = [4.0,4.0,3.0]; // wall thickness | |
| CradleRadius = 2.0; // corner rounding | |
| CradlePad = 1.0; // cushion on battery bottom | |
| BatteryBase = CradleWall.z + CradlePad; // actual bottom surface of battery | |
| CradleOA = [Battery.x + 2*CradleWall.x, | |
| min((Battery.y + 2*CradleWall.y),RackWidth), | |
| BatteryBase + Battery.z/2]; | |
| echo(str("Cradle OA: ",CradleOA)); | |
| module Cradle() { | |
| difference() { | |
| hull() | |
| for (i=[-1,1], j=[-1,1]) { // box with tidy rounded corners | |
| translate([i*(CradleOA.x/2 – CradleRadius), | |
| j*(CradleOA.y/2 – CradleRadius), | |
| 1*(CradleOA.z – CradleRadius)]) | |
| sphere(r=CradleRadius,$fn=6); | |
| translate([i*(CradleOA.x/2 – CradleRadius), | |
| j*(CradleOA.y/2 – CradleRadius), | |
| 0*(CradleOA.z/2 – CradleRadius)]) | |
| cylinder(r=CradleRadius,h=CradleOA.z/2,$fn=6); | |
| } | |
| translate([0,0,Battery.z/2 + BatteryBase]) // minus the battery | |
| minkowski(convexity=3) { // … slightly embiggened | |
| BatteryShape(); | |
| cube(2*CradlePad,center=true); | |
| } | |
| translate([0,0,CradleWall.z – ThreadThick + Protrusion/2]) // recess top legend | |
| cube([55,20,EmbossDepth],center=true); | |
| translate([0,0,(EmbossDepth – Protrusion)/2]) // recess bottom legend | |
| cube([70,15,EmbossDepth],center=true); | |
| } | |
| translate([0,4.0,CradleWall.z – DebossHeight – Protrusion]) | |
| linear_extrude(height=DebossHeight,convexity=20) | |
| text(text="PowerCore",size=6,spacing=1.20, | |
| font="Arial:style:Bold",halign="center",valign="center"); | |
| translate([0,-4.0,CradleWall.z – DebossHeight – Protrusion]) | |
| linear_extrude(height=DebossHeight,convexity=20) | |
| text(text="20K V2",size=6,spacing=1.20, | |
| font="Arial:style:Bold",halign="center",valign="center"); | |
| linear_extrude(height=DebossHeight,convexity=20) | |
| mirror([0,1,0]) | |
| text(text="KE4ZNU",size=10,spacing=1.20, | |
| font="Arial:style:Bold",halign="center",valign="center"); | |
| } | |
| //—– | |
| // Build things | |
| // Layouts for design & tweaking | |
| if (Layout == "Show") | |
| if (Part == "Battery") | |
| BatteryShape(); | |
| else if (Part == "Cradle") { | |
| Cradle(); | |
| translate([0,0,Battery.z/2 + CradleWall.z]) | |
| color(FadeColor,FadeAlpha) | |
| BatteryShape(); | |
| } | |
| else if (Part == "Pad") | |
| linear_extrude(height=CradlePad) | |
| projection(cut=true) | |
| BatteryShape(Jacks = false); | |
| // Build layouts for top-level parts | |
| if (Layout == "Build") { | |
| if (Part == "Cradle") | |
| Cradle(); | |
| if (Part == "Pad") | |
| projection(cut=true) | |
| BatteryShape(Jacks = false); | |
| } | |