Generic PCB Holder: Boost Power Supply

The DC-DC boost power supply for the LED needle lights has four mounting holes, two completely blocked by the heatsink and the others against components with no clearance for screw heads, soooo

3D printing to the rescue:

Boost converter - installed

Boost converter – installed

Now that the hulking ET227 operates in saturation mode, I removed the blower to make room for the power supply. Two strips of double-stick foam tape fasten the holder to the removable tray inside the Dell GX270’s case.

It’s basically a rounded slab with recesses for the PCB and clearance for solder-side components:

Boost converter mount - as printed

Boost converter mount – as printed

The solid model shows the screw holes sitting just about tangent to the PCB recess:

XW029 Booster PCB Mount

XW029 Booster PCB Mount

That’s using the new OpenSCAD with length scales along each axis; they won’t quite replace my layout grid over the XY plane, but they certainly don’t require as much computation.

I knew my lifetime supply of self-tapping hex head 4-40 screws would come in handy for something:

Boost converter in mount

Boost converter in mount

The program needs to know the PCB dimensions and how much clearance you want for the stuff hanging off the bottom:

PCBoard = [66,35,IntegerMultiple(1.8,ThreadThick)];

BottomParts = [[1.5,-1.0,0,0],	// xyz offset of part envelope
				[60.0,37.0,IntegerMultiple(3.0,ThreadThick)]];	// xyz envelope size (z should be generous)

That’s good enough for my simple needs.

The hole locations form a list-of-vectors that the code iterates through:

Holes = [			// PCB mounting screw holes: XY + rotation
		[Margin - ScrewOffset,MountBase[Y]/2,180/6],
		[MountBase[X] - Margin + ScrewOffset/sqrt(2),MountBase[Y] - Margin + ScrewOffset/sqrt(2),15],
		[MountBase[X] - Margin + ScrewOffset/sqrt(2),Margin - ScrewOffset/sqrt(2),-15],

... snippage ...

for (h = Holes) {
	translate([h[X],h[Y],-Protrusion]) rotate(h[Z])
		PolyCyl(Tap4_40,MountBase[Z] + 2*Protrusion,6);

That’s the first occasion I’ve had to try iterating a list and It Just Worked; I must break the index habit. The newest OpenSCAD version has Python-ish list comprehensions which ought to come in handy for something.

The “Z coordinate” of each hole position gives its rotation, so I could snuggle them up a bit closer to the edge by forcing the proper polygon orientation. The square roots in the second two holes make them tangent to the corners of the PCB, rather than the sides, which wasn’t true for the first picture. Fortunately, the washer head of those screws turned out to be just big enough to capture the PCB anyway.

The OpenSCAD source code:

// PCB mounting bracket for XW029 DC-DC booster
// Ed Nisley - KE4ZNU - January 2015

Layout = "Build";			// PCB Block Mount Build

//- Extrusion parameters must match reality!
//  Print with 4 shells and 3 solid layers

ThreadThick = 0.20;
ThreadWidth = 0.40;

HoleWindage = 0.2;			// extra clearance

Protrusion = 0.1;			// make holes end cleanly

AlignPinOD = 1.70;			// assembly alignment pins: filament dia

function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);

X = 0;						// useful subscripts
Y = 1;
Z = 2;

// Dimensions

inch = 25.4;

Tap4_40 = 0.089 * inch;
Clear4_40 = 0.110 * inch;
Head4_40 = 0.211 * inch;
Head4_40Thick = 0.065 * inch;
Nut4_40Dia = 0.228 * inch;
Nut4_40Thick = 0.086 * inch;
Washer4_40OD = 0.270 * inch;
Washer4_40ID = 0.123 * inch;

PCBoard = [66,35,IntegerMultiple(1.8,ThreadThick)];

BottomParts = [[1.5,-1.0,0,0],				// xyz offset of part envelope
				[60.0,37.0,IntegerMultiple(3.0,ThreadThick)]];			// xyz envelope size (z should be generous)

Margin = IntegerMultiple(Washer4_40OD,ThreadWidth);

MountBase = [PCBoard[X] + 2*Margin,
			PCBoard[Y] + 2*Margin,
			IntegerMultiple(5.0,ThreadThick) + PCBoard[Z] + BottomParts[1][Z]
echo("Mount base: ",MountBase);

ScrewOffset = Clear4_40/2;

Holes = [									// PCB mounting screw holes: XY + rotation
		[Margin - ScrewOffset,MountBase[Y]/2,180/6],
		[MountBase[X] - Margin + ScrewOffset/sqrt(2),MountBase[Y] - Margin + ScrewOffset/sqrt(2),15],
		[MountBase[X] - Margin + ScrewOffset/sqrt(2),Margin - ScrewOffset/sqrt(2),-15],

CornerRadius = Washer4_40OD / 2;

// Useful routines

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,

module ShowPegGrid(Space = 10.0,Size = 1.0) {

  RangeX = floor(100 / Space);
  RangeY = floor(125 / Space);

	for (x=[-RangeX:RangeX])
	  for (y=[-RangeY:RangeY])


// Build things

module PCB() {

	union() {
		translate(BottomParts[X] - [0,0,BottomParts[1][Z]])
			cube(BottomParts[Y] + [0,0,Protrusion]);


module Block() {
			for (i = [-1,1], j = [-1,1])
				translate([i*(MountBase[X]/2 - CornerRadius),j*(MountBase[Y]/2 - CornerRadius)],0)
					cylinder(r=CornerRadius,h=MountBase[Z] - Protrusion,$fn=8*4);

module Mount() {

	difference() {

		translate([MountBase[X]/2 - PCBoard[X]/2 + BottomParts[0][X] - Protrusion,
					MountBase[Z] - PCBoard[Z] - BottomParts[1][Z]])
			cube([BottomParts[1][X] + 2*Protrusion,

		translate([MountBase[X]/2 - PCBoard[X]/2,		// PCB recess
					MountBase[Y]/2 - PCBoard[Y]/2,
					MountBase[Z] - PCBoard[Z]])
		for (h = Holes) {
			translate([h[X],h[Y],-Protrusion]) rotate(h[Z])
				PolyCyl(Tap4_40,MountBase[Z] + 2*Protrusion,6);



if (Layout == "PCB")

if (Layout == "Block")

if (Layout == "Mount")

if (Layout == "Build")


  1. #1 by Ken Davidson on 2015-01-29 - 09:15

    I’ve been wondering for months now: is there a purpose behind the row of plastic that goes around the perimeter of each of your 3D prints? Is it just a sanity check to ensure the finished part will fit on the bed before getting too far into the print?

    • #2 by Ed on 2015-01-29 - 09:41

      That’s the “skirt” to stabilize the extruder pressure before starting the real object.

      The hot end dribbles plastic while it’s heating up, so it starts with an air bubble at zero pressure. The initial G-Code positions the nozzle at the front edge of the platform, extrudes 30 mm (!) of filament to refill the hot end, wipes its nose on the platform, then zips off to extrude the skirt.

      The first skirt loop (of at least three) generally looks pretty ragged, but by the last loop the extruder will be laying down a perfect track, exactly 0.4 mm wide and 0.2 mm thick. Unlike most folks, I set the first layer thread to match all the others, neither squished nor overfilled, although it chugs along at 25 mm/s for good platform adhesion.

      The will-it-fit “sanity check” happens in the slicer, which knows the platform size & shape. In principle, it should tell you when you’ve screwed up, but I tend to be a bit (well, more than a bit) compulsive about aligning the model properly to begin with… [grin]

      • #3 by Jason Doege on 2015-01-29 - 09:55

        Regarding the will it fit sanity check. The newest version of slic3r for the Mac didn’t respect the .ini file that describes the platform coordinate system and bounds. The print immediately sent the print head to an impossible location and steppers began complaining as only steppers do.

        I suggested the makers of the printer that the g-code interpreter could, you know, detect when an illegal move was about to be made and shut things down instead of having the printer grind away at its limits. I received an odd answer that suggested that would break the tool chain; that the g-code interpreter’s job is to do just what it is told. Yeah, no. Not a good answer in my book.

        • #4 by Ed on 2015-01-29 - 10:16

          instead of having the printer grind away at its limits

          Well, it doesn’t to any real damage, so you can regard that horrible sound as a built-in alarm buzzer.

          That said, I have plenty of justification for being so compulsive. That’s my story and I’m sticking with it.

          The Arduino at the core of all the 3D printer firmware just isn’t up to the demands we’re placing on it, but that doesn’t stop us from wanting more and more capabilities. The fact that there’s no user interface, any reasonable way to communicate with the poor thing, nor any code space for new features, does tend to bound the mmmm solution space.

          • #5 by Jason Doege on 2015-01-30 - 09:09

            It doesn’t do any real damage until it’s the z-bound that is disrespected by crashing the melty-hot print head into the sanded acrylic print bed, leaving behind a charred crater. BTW, matte acrylic seems to have excellent adhesion with PLA, no goop or heated print bed needed.

            • #6 by Ed on 2015-01-30 - 11:25

              leaving behind a charred crater

              That’s another alert: you used the wrong bed material. [grin]

              The Thing-o-Matic’s aluminum plates sport nasty scars from that very problem, so I share your pain.

              The whole what-sticks-to-what debate goes on. From what I see on the MakerGear forum, two people using the same goop on the same glass plate with the same slicer profile get entirely different adhesion, so there’s certainly room for more materials science.

  2. #7 by RL on 2015-01-29 - 10:13

    In the alarm system industry, a company called Altronix sells something called Snap Track for mounting circuit boards. It’s very similar to the idea you came up with, but requires no screws. Several sizes are available (e.g. ST1, ST2, ST3).

    • #8 by Ed on 2015-01-29 - 10:20

      Eventually, you have PCB holders for DIN mounting rails.

      Ain’t nothing new under the sun, sez I…