Posts Tagged M2

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,
           h=Height,
           $fn=Sides);
}

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

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

	for (x=[-RangeX:RangeX])
	  for (y=[-RangeY:RangeY])
		translate([x*Space,y*Space,Size/2])
		  %cube(Size,center=true);

}

//----------------------
// Build things

module PCB() {

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

}

module Block() {
	translate([MountBase[X]/2,MountBase[Y]/2,0])
		hull()
			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() {
		Block();

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

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

}

//ShowPegGrid();

if (Layout == "PCB")
	PCB();

if (Layout == "Block")
	Block();

if (Layout == "Mount")
	Mount();

if (Layout == "Build")
	translate([-MountBase[X]/2,-MountBase[Y]/2,0])
	Mount();

,

8 Comments

Monthly Image: Spherometer Measurements

Our Larval Engineer volunteered to convert the lens from a defunct magnifying desk lamp into a hand-held magnifier; there’s more to that story than is relevant here. I bulldozed her into making a solid model of the lens before starting on the hand-holdable design, thus providing a Thing to contemplate while working out the holder details.

That justified excavating a spherometer from the heap to determine the radius of curvature for the lens:

Student Sphereometer on lens

Student Sphereometer on lens

You must know either the average radius / diameter of the pins or the average pin-to-pin distance. We used a quick-and-dirty measurement for the radius, but after things settled down, I used a slightly more rigorous approach. Spotting the pins on carbon paper (!) produced these numbers:

Sphereometer Pin Radii

Sphereometer Pin Radii

The vertical scale has hard-metric divisions: 1 mm on the post and 0.01 on the dial. You’d therefore expect the pins to be a hard metric distance apart, but the 25.28 mm average radius suggests a crappy hard-inch layout. It was, of course, a long-ago surplus find without provenance.

The 43.91 mm average pin-to-pin distance works out to a 50.7 mm bolt circle diameter = 25.35 mm radius, which is kinda-sorta close to the 25.28 mm average radius. I suppose averaging the averages would slightly improve things, but …

The vertical distance for the lens in question was 0.90 mm, at least for our purposes. That’s the sagitta, which sounds cool enough to justify this whole exercise right there. It’s 100 mm in diameter and the ground edge is 2.8 mm thick, although the latter is subject to some debate.

Using the BCD, the chord equation applies:

  • Height m = 0.90 mm
  • Base c = 50.7 mm
  • Lens radius r = (m2 + c2/4) / 2m = 357.46 mm

Using the pin-to-pin distance, the spherometer equation applies:

  • Pin-to-pin a = 43.91 mm
  • Sagitta h = 0.90 mm
  • Lens radius R = (h/2) + (a2 / 6h) = 357.50 mm

Close enough, methinks.

Solving the chord equation for the total height of each convex side above the edge:

  • Base c = 100 mm
  • Lens radius r = 357.5 mm
  • Height m = r – sqrt(r2 -c2/4) = 3.5 mm

So the whole lens should be 2 · 3.5 + 2.8 = 9.8 mm thick. It’s actually 10.15 mm, which says they were probably trying for 10.0 mm and I’m measuring the edge thickness wrong.

She submitted to all this nonsense with good grace and cooked up an OpenSCAD model that prints the “lens” in two halves:

Printed Lens - halves on platform

Printed Lens – halves on platform

Alas, those thin flanges have too little area on the platform to resist the contraction of the plastic above, so they didn’t fit together very well at all:

Printed Lens - base distortion

Printed Lens – base distortion

We figured a large brim would solve that problem, but then it was time for her to return to the hot, fast core of college life…

,

Leave a comment

3D Printed Handcuffs

A friend who read about my chain mail armor asked about handcuffs, so I ran off one of gianteye’s Printable Handcuffs V1.0:

3D Printed Handcuff

3D Printed Handcuff

Alas, that shows the difficulty of using an STL file designed for a different printer, as the interlocking parts didn’t even come close to fitting and required major abrasive adjustment with a Dremel. One of the few successful prints reported on Thingiverse seems involve a commercial printer, so it’s not just the M2’s problem.

I’m not sufficiently motivated to conjure an OpenSCAD model right now…

Leave a comment

3D Printed Chain Mail Armor – Zombie Hunter Edition

Reducing the link bars to 4×4 threads produced a diminutive patch:

Square Armor - small links - platform

Square Armor – small links – platform

Most of the dark smudges come from optical effects in the natural PLA filament, but the second-from-upper-left armor button contains a dollop of black PLA left in the nozzle from the end of that spool; running meters and meters of filament through the extruder isn’t enough to clean the interior. I now have some filament intended to clean the extruder, but it arrived after the black ran out.

Comparing the patch with the original buttons shows the size difference:

Square Armor - large vs small links

Square Armor – large vs small links

A trial fit suggested a 5×5 patch would fit better, so …

Square Armor - small links - mounted

Square Armor – small links – mounted

The whip stitching accentuates the jacket’s style.  We I think a glittery piping cord square around the armor links would spiff it up enormously and hide the open links, but that’s in the nature of fine tuning.

I’ll eventually see what happens with 3×3 thread = 1.2×0.6 mm links, which may be too small for reliable bridging and too delicate for anything other the finest evening wear.

,

Leave a comment

Rounded Cable Clips

This isn’t quite the smoothly rounded clip I had in mind:

LED Cable Clip - rounded channel

LED Cable Clip – rounded channel

It seems somewhat better looking than the square design, though:

LED Cable Clips

LED Cable Clips

I ran off a few of both styles to have some on hand:

Cable clips - on platform

Cable clips – on platform

They’re in a bag until I install the new LED strips and needle light.

The OpenSCAD source code:

// LED Cable Clips
// Ed Nisley - KE4ZNU - October 2014

Layout = "Oval";			// Oval Square Build

//- Extrusion parameters must match reality!

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);

//----------------------
// Dimensions

Base = [12.0,12.0,IntegerMultiple(2.0,ThreadThick)];	// base over sticky square

CableOD = 2.0;

BendRadius = 3.0;

Bollard = [BendRadius,(sqrt(2)*Base[0]/2 - CableOD - BendRadius),2*CableOD];
B_BOT = 0;
B_TOP = 1;
B_LEN = 2;

NumSides = (Shape == "Square") ? 5*4 : 6*3;

//----------------------
// 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,
           h=Height,
           $fn=Sides);
}

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

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

	for (x=[-RangeX:RangeX])
	  for (y=[-RangeY:RangeY])
		translate([x*Space,y*Space,Size/2])
		  %cube(Size,center=true);

}

//-- Square clip with central bollard

module SquareBollard() {

	intersection() {
		translate([0,0,(Base[2] + Bollard[B_LEN])/2])			// overall XYZ outline
			cube(Base + [0,0,Bollard[2]],center=true);

		union() {
			translate([0,0,Base[2]/2])						// oversize mount base
				scale([2,2,1])
					cube(Base,center=true);

			for (i=[-1,1] , j=[-1,1]) {						// corner bollards
				translate([i*Base[0]/2,j*Base[1]/2,(Base[2] - Protrusion)])
					rotate(180/NumSides)
					cylinder(r=Bollard[B_BOT],h=(Bollard[B_LEN] + Protrusion),center=false,$fn=NumSides);

			translate([0,0,(Base[2] - Protrusion)])			// center tapered bollard
				cylinder(r1=Bollard[B_BOT],r2=Bollard[B_TOP],
						 h=(Bollard[B_LEN] + Protrusion),
						 center=false,$fn=NumSides);
			}
		}
	}

}

//-- Oval clip with central passage

module OvalPass() {

	intersection() {
		translate([0,0,(Base[2] + Bollard[B_LEN])/2])		// overall XYZ outline
			cube(Base + [0,0,2*CableOD],center=true);

		union() {
			translate([0,0,Base[2]/2])						// oversize mount base
				scale([2,2,1])
					cube(Base,center=true);

			for (j=[-1,1])									// bending ovals
				translate([0,j*Base[1]/2,(Base[2] - Protrusion)])
					resize([Base[0]/0.75,0,0])
						cylinder(d1=0.75*(Base[1]-CableOD),d2=(Base[1]-CableOD)/cos(180/NumSides),
								h=(Bollard[B_LEN] + Protrusion),
								center=false,$fn=NumSides);
		}
	}
/*
#	translate([0,0,6])
		rotate([0,90,0])
			cylinder(d=CableOD,h=10,center=true,$fn=48);
*/
}

//----------------------
// Build it

ShowPegGrid();

if (Layout == "Square")
	SquareBollard();

if (Layout == "Oval")
	OvalPass();

,

Leave a comment

OpenSCAD: Quantized Vertices

Back when I started fiddling with 3D printed chain mail, the whole process from model to plastic worked wonderfully well. That continued with the larger sheets, but now, occasionally, the OpenSCAD model would produce weirdly sliced links. Depending on nothing repeatable, some links wouldn’t bridge correctly: the thread paths in the bottom layer across the gap would mysteriously stop just short of one pillar, return to the start, and leave an unsupported shelf that would, of course, fall into the gap.

Shortly before Christmas, I managed to get a consistent failure that manifested differently: upon loading the STL file, Slic3r would quietly perform dozens of automatic corrections that (sometimes!) produced bizarrely distorted results. Feeding a failing model into Meshlab showed an irregular assortment of “self intersecting faces”, highlighted in red:

Chain Mail Square Armor - open - 2x2 - Meshlab self-intersecting faces

Chain Mail Square Armor – open – 2×2 – Meshlab self-intersecting faces

Although all four outer links in that image come from the same OpenSCAD module with identical sizes, they don’t all exhibit the same problem in the (nominally identical) faces on each of their four corners. In fact, those faces come from the intersection of two square slabs, carefully sized and positioned to avoid creating coincident planes:

Chain Mail Link - Outer shape

Chain Mail Link – Outer shape

The central opening comes from a similar, slightly smaller, intersected-squares shape, but all four interior corner faces in each link show that they’re self-intersecting.

The STL looked fine in Meshlab, except for the highlit self-intersecting faces, so the geometry seemed OK.

When Slic3r autocorrected the “problems”, it apparently removed one vertex on the bottom surface of each bar, deleted the triangles connected to that vertex, then repaired the mesh to produce a delightfully symmetric pattern:

Chain Mail Square Armor - open - 2x2 - Slic3r corrections

Chain Mail Square Armor – open – 2×2 – Slic3r corrections

Although the links are resolutely symmetric, Slic3r seemed happy with the identical vertices at the other end of the bar.

Unfortunately, the resulting G-Code won’t produce good links:

Chain Mail Square Armor - open - 2x2 - first layer G-code visualization

Chain Mail Square Armor – open – 2×2 – first layer G-code visualization

So, shortly before Christmas, I filed an issue on OpenSCAD’s Github repository.

The ensuing discussion showed that Meshlab flags faces as “self intersecting” when they have different vertices, even if their values are numerically equal, as well as vertices that differ by teeny amounts. Slic3r applies slightly different criteria to vertices & faces when it automagically corrects “problems” in the STL file, so that Meshlab may:

  • Highlight faces that don’t bother Slic3r
  • Apply the same highlight to faces that cause horrible problems

I don’t profess to understand much of that and may have the details wrong, but, apparently, OpenSCAD formerly used quantized coordinates that ensured all vertices within a tiny volume would have the same numeric value. In particular, all three faces that meet at a common point would, in fact, have numerically equal coordinate values for that point. The STL file format consists of a list of separate triangles, each with three coordinates for each of the three axes, and (without quantization) it was entirely possible for each of the three triangles with a common point to have three very slightly different positions for that point.

In theoretic terms, quantized coordinates cause horrible problems during geometric manipulation, because numeric values that aren’t exact can make repeated transformations come out wrong; running an object through a transformation and it’s inverse might not yield an object identical to the original one.

In practical terms, it seems that slicers and STL repair algorithms can reach incorrect conclusions based on minute differences produced by floating-point operations and numeric-to-text conversions. Those differences depend on slight changes in position, rotation, and size, so doing anything to the model produces completely different results.

That notwithstanding, the day after Christmas brought a new OpenSCAD version that uses quantized coordinates. A bit of rummaging in the source shows that the 3D grid (defined in src/grid.h) isn’t all that coarse:

const double GRID_FINE   = 0.00000095367431640625;

STL files don’t carry units, so that could be in either millimeters (the Slic3r / RepRap convention) or inches (Sketchup, but we won’t go there). It’s exactly 1/10242, in case you were wondering, which produces a 5% speedup in the geometry engine compared to the more human-readable 1/10002.

With that commit in hand, all the chain mail links slice perfectly again.

A very nice Christmas present, indeed!

Thanks, Marius…

, ,

4 Comments

MakerGear M2: Better Lighting, Redux

A surplus haul of 24 V / 150 mA white LED panels arrived:

LED Panel - 24 V 150 mA

LED Panel – 24 V 150 mA

I wired a pair to a 24 V wall wart and stuck them under the M2’s bridge supporting the X stage:

LED Panel - on M2 Gantry

LED Panel – on M2 Gantry

I thought about epoxying them in place to get better heatsinking to the metal bridge. The ever-trustworthy description said the big copper baseplate meant the panels didn’t need any heatsinking, so I used tapeless sticky and will hope for the best. Should the sticky give out, then I’ll use epoxy.

They’re much better than the previous white LED strip, although it’s tough to tell in the pictures. The chain mail armor appears under the new lights; some older pictures will creep in from time to time.

,

5 Comments