Posts Tagged CNC

Chocolate Molds: Closeups

An overall view of the mold:

Tux Gradient 4x4 - mold separated

Tux Gradient 4×4 – mold separated

The PLA positive, after removing the silicone negative, showing the silicone below the surface:

Tux Gradient - PLA positive detail

Tux Gradient – PLA positive detail

The corresponding silicone negative cavity, flipped top-to-bottom:

Tux Gradient - silicone negative detail

Tux Gradient – silicone negative detail

The milk chocolate result, although probably not from the same cavity:

Tux Gradient - milk chocolate detail

Tux Gradient – milk chocolate detail

The radial gradient on the tummy comes through clearly and, I think, pleasingly, even though it’s only a few layers tall. The threads defining the flipper just above (to the left, in these images) of the foot show where the flipper crosses the tummy and foot level. I didn’t expect the foot webbing grooves to get that ladder-like texture, but I suppose having non-slip foot treads would be an advantage.

If you don’t mind the hand-knitted texture, which I don’t, this process seems perfectly workable.

About these ads

,

Leave a comment

Chocolate Molds: Acrylic Base

Although directly printing the 2×2 molds worked reasonably well, that does not scale to larger arrays, because OpenSCAD doesn’t handle the profusion of vertices with any grace. Duplicating the STL file created from the height map image, however, isn’t a problem:

Tux-Gradient - Slic3r layout

Tux-Gradient – Slic3r layout

I actually did it in two passes: 4 molds to be sure they’d come out right, then another dozen. Figure a bit under two hours for the lot of them, no matter how you, ah, slice it.

A grid drawn directly on 1/16 inch = 1.5 mm acrylic sheet guided the layout:

Tux Gradient 4x4 - mold as-cast

Tux Gradient 4×4 – mold as-cast

I anointed the back of each mold positive with PVC pipe cement, the version with tetrahydrofuran to attack the PLA and acetone/MEK to attack the acrylic, lined it up, and pressed it in place. The positives have recesses for alignment pins, but even I think that’s overkill in this application.

Memo to Self: Flip the acrylic over before gluing, so the guide lines wipe neatly off the bottom.

Tape a cardboard frame around the acrylic, mix & pour the silicone, put it on the floor to ensure it’s level (unlike our kitchen table), wait overnight for the cure, then peel positive and negative apart:

Tux Gradient 4x4 - mold separated

Tux Gradient 4×4 – mold separated

As before, the top surface of the positives isn’t watertight, so the silicone flowed through into the molds. This isn’t a simple extruder calibration issue, because the thinwall boxes are spot on, all the exterior dimensions are accurate, and everything else seems OK. What’s not OK is that threads on the top and (now that I look at it) bottom surfaces aren’t properly joining.

A closeup of the positive shows silicone between the threads and under the surface:

Tux Gradient 4x4 - postive detail

Tux Gradient 4×4 – postive detail

But the negative silicone looks just fine, in the usual hand-knitted way of all 3D printed parts:

Tux Gradient 4x4 - negative detail

Tux Gradient 4×4 – negative detail

Definitely fewer bubbles than before, although the flange between the flippers (wings? whatever) and the body isn’t as clean as it could be. Doing better may require pulling a vacuum on the silicone, which would mean the positives really must be air-tight solids.

Anyhow, the acrylic base produced a wonderfully flat surface that should make it a lot easier to run a scraper across the chocolate to remove the excess. Not that excess chocolate is ever a problem, but it’s the principle of the thing.

, ,

5 Comments

Chocolate Molds: Improved Tux Height Map

This is the simple height-map Tux image I’d been using for the chocolate molds:

Tux_Hi_Profile

Tux_Hi_Profile

But the poor critter looks a bit flattened:

Tux_Hi_Profile - solid model

Tux_Hi_Profile – solid model

The final result is tastier, but gives off a roadkill vibe:

Tux chocolates - detail

Tux chocolates – detail

After a few tweaks to the image, now he has a radial gradient on his tummy, his right flipper extends forward, his feet have webs, and his smile looks radiant. The gray levels now extend over a larger range with a bit more separation, with the intent that he’ll now be 5 mm thick:

Tux-Gradient

Tux-Gradient

Converted to a solid model in OpenSCAD:

Tux-Gradient - Solid Model

Tux-Gradient – Solid Model

In his STL file garb, he’s lookin’ pretty good:

Tux-Gradient - Solid Model - STL

Tux-Gradient – Solid Model – STL

Next step: plastic!

,

Leave a comment

Laser-photodiode Beam-Break Sensor Fixture

The game plan: drop a small object through a laser beam that shines on a photodiode, thus causing an electrical signal that triggers various flashes and cameras and so forth and so on. This fixture holds the laser and photodiode in the proper orientation, with enough stability that you (well, I) can worry about other things:

Laser-photodiode fixture - on blade

Laser-photodiode fixture – on blade

It’s mounted on the blade of a dirt-cheap 2 foot machinist’s square clamped to the bench which will probably get a few holes drilled in its baseplate for more permanent mounting.

The solid model looks about like you’d expect:

Laser-photodiode fixture - solid model

Laser-photodiode fixture – solid model

There’s a small hole in the back for an 8-32 setscrew that locks it to the blade; the fit turned out snug enough to render the screw superfluous. I added those two square blocks with the holes after I taped the wires to the one in the picture.

The two semicircular (well, half-octagonal) trenches have slightly different diameters to suit the heatshrink tubing around the photodiode (a.k.a., IR LED) and brass laser housing. A dab of fabric adhesive holds the tubes in place, in addition to the Gorilla Tape on the ends.

The laser came focused at infinity, of course. Unscrewing the lens almost all the way put the focus about 3/4 of the way across the ring; call it 40 mm. The beam is rectangular, about 1×2 mm, at the center of the ring, and I rotated the body to make the short axis vertical; that’s good enough for my purposes.

The cable came from a pair of cheap earbuds with separate Left/Right pairs all the way from the plug.

The model builds in one piece, of course, and pops off the platform ready to use:

Laser-photodiode fixture - on platform

Laser-photodiode fixture – on platform

If you were doing this for an analytic project, you’d want a marker for the beam centerline on the vertical scale, but that’s in the nature of fine tuning. As it stands, the beam sits 8 mm above the base and flush with the top surface of the ring; if that were 10 mm, it’d be easier to remember.

The OpenSCAD source code has a few tweaks and improvements:

// Laser and LED-photodiode break-beam sensor
// Ed Nisley - KE4ZNU - March 2014

Layout = "Show";			// Build Show Ring Mount Guide

//- Extrusion parameters must match reality!
//  Print with 2 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

inch = 25.4;

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

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

LaserOD = 6.0;				// brass focus tube
LaserLength = 20.0;			//  ... wire clearance

SensorOD = 6.5;				// including light shield
SensorLength = 20.0;		//  ... wire clearance

RingSize = [50.0,70.0,8.0,8*4];	// support ring dimensions
RING_ID = 0;
RING_OD = 1;
RING_THICK = 2;
RING_SIDES = 3;

StrutWidth = 2.5;					// strut supporting this thing
StrutLength = 26.5;

StrutBlock = [10.0,35.0,20.0];		// block around the clearance slot
BLOCK_WIDTH = 0;
BLOCK_LENGTH = 1;
BLOCK_HEIGHT = 2;

StrutScrewTap = 2.7;				// 6-32 SHCS

GuideID = 4.0;						// guide for cables
GuideOD = 3*GuideID;

BuildSpace = 3.0;					// spacing between objects on platform

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

}

module Ring() {

	difference() {
		union() {
			rotate(180/RingSize[RING_SIDES])
				cylinder(d=RingSize[RING_OD],h=RingSize[RING_THICK],
						$fn=RingSize[RING_SIDES]);
			translate([-LaserOD,(-LaserLength - RingSize[RING_ID]/2),0])
				cube([2*LaserOD,LaserLength,RingSize[RING_THICK]],center=false);
			translate([-SensorOD,(-0*SensorLength + RingSize[RING_ID]/2),0])
				cube([2*SensorOD,SensorLength,RingSize[RING_THICK]],center=false);
		}
		rotate(180/RingSize[RING_SIDES])
			translate([0,0,-Protrusion])
				cylinder(d=RingSize[RING_ID],h=(RingSize[RING_THICK] + 2*Protrusion),
						$fn=RingSize[RING_SIDES]);
		translate([0,0,RingSize[RING_THICK]])
			rotate([90,0,0]) rotate(180/8)
				PolyCyl(LaserOD,3*LaserLength,8);
		translate([0,0,RingSize[RING_THICK]])
			rotate([-90,0,0]) rotate(180/8)
				PolyCyl(SensorOD,3*SensorLength,8);
	}
}

module Mount() {
	translate([0,0,StrutBlock[2]/2])
		difference() {
			cube(StrutBlock,center=true);
			cube([StrutWidth,StrutLength,2*StrutBlock[2]],center=true);
			translate([0,-StrutLength/3,0])
				rotate([90,0,0])
					PolyCyl(StrutScrewTap,StrutLength/2,6);
		}
}

module Guide() {

	difference() {
		translate([0,0,RingSize[RING_THICK]/2])
			cube([GuideOD,GuideOD,RingSize[RING_THICK]],center=true);
		translate([0,0,-Protrusion]) rotate(180/8)
			PolyCyl(GuideID,(RingSize[RING_THICK] + 2*Protrusion),8);
	}
}

module Assembly() {
	Ring();
	translate([(RingSize[RING_OD]/2 + StrutBlock[BLOCK_LENGTH]/2
				- (StrutBlock[BLOCK_LENGTH] - StrutLength)/2) + Protrusion,0,0])
		rotate(90)
			Mount();
	for (i=[-1,1])
		translate([(RingSize[RING_OD]/2 + GuideID/2),
				  i*(StrutBlock[BLOCK_WIDTH]/2 + GuideID),
				  0])
			Guide();
}

//- Build it

ShowPegGrid();

if (Layout == "Ring") {
	Ring();
}

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

if (Layout == "Guide") {
	Guide();
}

if (Layout == "Show") {
	Assembly();
}

if (Layout == "Build") {

	translate([-5/2,-5/2,0])
		cube(5);
}

,

7 Comments

Halogen Desk Lamp: LED Floodlight Retrofit

Quite a while ago, I rebuilt a gooseneck shop lamp with an LED floodlight module, the light from which appears in many pictures of the Sherline mill. That module has a sibling that I just combined with a defunct halogen desk lamp to produce a better task light for the bench; the original 12 VAC 50 W transformer now loafs along at 4 W and ballasts the lamp base against tipping.

My initial idea, of course, was a 3D printed adapter from the existing arm hardware to the LED module, but PLA gets droopy at  normal high-intensity LED heatsink temperatures. That led to doodling a metal bracket around the LED module flange, which led to pondering how annoying that would be to make, which led to the discovery that the screws holding the LED plug to the heatsink were ordinary M2x0.4 Philips head, which suggested I could just screw a bracket to the back of the module, which brought a recently harvested aluminum heatsink to hand, which led to the discovery that the tip of the pivot screw fit perfectly between the fins, which …

Shortly thereafter, I milled off the central fins to fit the shaft of the pivot screw, introduced the heatsink to Mr. Disk Sander to bevel the bottom, sawed the threads off the pivot, press-fit the two together, drilled a 2 mm cross-hole into the pivot, buttered it all up with epoxy, jammed a short M2 screw into the cross hole, and let the whole mess cure:

Desk Lamp LED Adapter - top view

Desk Lamp LED Adapter – top view

The lamp modules were a surplus find, with one pin clipped nearly flush to the insulator. I soldered a pair of the same male pins as in the battery holders, with the matching female pins as a crude connector. The unshrunk heatstink tubing isn’t lovely, but got us to First Light:

Desk Lamp LED Adapter - front view

Desk Lamp LED Adapter – front view

The original counterweight is, of course, much too heavy for the dinky LED module, so I’ll drill the mounting hole for the vertical arm further back on the beam to get another foot of reach. That will require more wire between the transformer to the lamp, soooo the connectors might just become soldered joints.

As you can tell from the background, Mary snatched the lamp from my hands and put it to immediate use in The Quilting Room.

The original doodles bear no resemblance to the final product, but do have some key dimensions that (having discarded the unused hardware) I’ll likely never need again.

The pivot between the arm and the lamp housing, with an idea for the LED holder:

Desk Lamp Bracket Dimensions - doodle

Desk Lamp Bracket Dimensions – doodle

Details of the repurposed heatsink and the pivot bolt, with a block that never got built:

Desk Lamp Heatsink Dimensions - doodle

Desk Lamp Heatsink Dimensions – doodle

, , ,

3 Comments

Browning Hi-Power Magazine Capacity Reduction: Blocks

After a bit of trial fitting and tweaking, just three parameters cover the variations for the magazines in hand:

  • Offset of screw from front-to-back center
  • Height of spring retaining crimp
  • Distance between screw and crimp

Collecting those numbers in a single array, with constants to select the entries, makes some sense:

//BlockData =  [-0.5, 1.5, 11.5];	// Browning OEM
BlockData = [-1.5, 2.0, 9.0];		// Generic 1

SCREWOFFSET = 0;
CRIMPHEIGHT = 1;
CRIMPDISTANCE = 2;

Although commenting out an undesired variable isn’t fashionable, OpenSCAD doesn’t have a practical mechanism to set specific values based on a control variable:

  • if-then-else deals with geometric objects
  • (boolean)?when_true:when_false (the ternary operator) doesn’t scale well

You could, of course, depend on OpenSCAD’s behavior of using the last (in syntactic order) instance of a “variable”, but IMHO that’s like depending on semantic whitespace.

In any event, the rest of the block builds itself around those three values by recomputing all of its dimensions.

The Browning OEM block looks like this:

Browning Hi-Power Magazine Block - solid model - BHP OEM

Browning Hi-Power Magazine Block – solid model – BHP OEM

The Generic floorplate has a much larger spring retaining crimp, so the block has far more overhang:

Browning Hi-Power Magazine Block - solid model - Generic 1

Browning Hi-Power Magazine Block – solid model – Generic 1

As before, the yellow widgets are built-in support structures separated from the main object by one thread thickness and width. That seems to maintain good vertical tolerance and allow easy removal; the structures snap free with minimal force. A closeup look shows the gaps:

Browning Hi-Power Magazine Block - solid model - Generic 1 - support detail

Browning Hi-Power Magazine Block – solid model – Generic 1 – support detail

The main shape now has a 2 mm taper to ease the magazine spring past the upper edge of the block. The horn remains slightly inset from the side walls to ensure that the whole thing remains manifold:

Browning Hi-Power Magazine Block - solid model - Generic 1 - whole end

Browning Hi-Power Magazine Block – solid model – Generic 1 – whole end

The whole object looks about the same, though:

Browning Hi-Power Magazine Block - solid model - Generic 1 - whole side

Browning Hi-Power Magazine Block – solid model – Generic 1 – whole side

The shape descends from the geometry I used for the stainless steel block, with the additional internal channel (on the right in the models) to be filled with steel-loaded epoxy during assembly. That should make the whole block sufficiently robust that you must destroy the floorplate and distort the spring to get it out; wrecking the magazine’s innards should count as not “readily” modifiable.

Some destructive testing seems to be in order…

The OpenSCAD source code:

// Browning Hi-Power Magazine Plug
// Ed Nisley KE4ZNU December 2013
//	February 2014 - easier customization for different magazine measurements

Layout = "Whole";			// Whole Show Split
							//  Whole = upright for steel or plastic
							//  Show = section view for demo, not for building
							//  Split = laid flat for plastic show-n-tell assembly

AlignPins = true && (Layout == "Split");	// pins only for split show-n-tell

Support = true && (Layout != "Split");		// no support for split, optional otherwise

// Define magazine measurements

//BlockData =  [-0.5, 1.5, 11.5];		// Browning OEM
BlockData = [-1.5, 2.0, 9.0];		// Generic 1

SCREWOFFSET = 0;
CRIMPHEIGHT = 1;
CRIMPDISTANCE = 2;

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

ThreadThick = 0.20;
ThreadWidth = 0.40;

HoleWindage = 0.2;

Protrusion = 0.1;			// make holes end cleanly

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

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

Angle = 12.5;				// from vertical

SpringID = 10.3;			// magazine spring curvature (measure with drill shank)
SpringRadius = SpringID / 2;
Taper = 2.0;				// total taper toward top

Length = 24.5;				// front-to-back perpendicular to magazine shaft
Height = 17.0;				// bottom-to-top, parallel to magazine shaft

RectLength = Length - SpringID;	// block length between end radii

HornBaseOD = 8.0;			// fits between follower pegs to prevent shortening
HornTipOD = 5.0;
HornAddTip = (HornTipOD/2)*tan(Angle);
HornAddBase = (HornBaseOD/2)*tan(Angle);
HornAddLength = HornAddTip + HornAddBase + 2*Protrusion;
HornLength = 12.0;			// should recompute ODs, but *eh*

ScrewOD = 3.0 - 0.25;		// screw hole dia - minimal thread engagement
ScrewLength = Height - 5.0;
ScrewOffset = BlockData[SCREWOFFSET];	//   ... from centerline on XY plane

NutOD = 5.8;						// hex nut dia across flats
NutThick = 2.4;						//  ... generous allowance for nut
NutTrapLength = 1.5*NutThick;		// allow for epoxy buildup
NutTrapBaseHeight = 5.0;			//  ... base height from floor plate

CrimpHeight = IntegerMultiple(BlockData[CRIMPHEIGHT],ThreadThick);		// vertical clearance for spring crimp tab on base plate

CrimpDistance = BlockData[CRIMPDISTANCE];		//  ... clip to screw hole center
CrimpOffset = -(CrimpDistance - ScrewOffset);	// ... horizontal from centerline

SupportLength = 4.0;		// length of support struts under Trim
SupportWidth = IntegerMultiple(0.9*SpringID,4*ThreadWidth);	// ... size needed for platform adhesion
SupportThick = CrimpHeight - ThreadThick;	// ... clearance for EZ removal

VentDia = 2.5;				// air vent from back of screw recess
//VentOffset = CrimpOffset + VentDia/2 + 5*ThreadWidth;
VentOffset = -(NutOD + 4*ThreadWidth);
VentLength = ScrewLength + VentDia;

RecessDia = 3.5;			// additional air vent + weight reduction
RecessLength = ScrewLength + RecessDia/2;		//  ... internal length
RecessOffset = Length/2 - RecessDia/2 - 5*ThreadWidth;	//  ... offset from centerline

PinOD = 1.72;				// alignment pins
PinLength = 4.0;
PinInset = 0.6*SpringRadius;	// from outside edges
echo(str("Alignment pin length: ",PinLength));

NumSides = 8*4;				// default cylinder sides

Offset = 5.0/2;				// from centerline for build layout

//----------------------
// Useful routines

function Delta(a,l) = l*tan(a);				// incremental length due to angle

// Locating pin hole with glue recess
//  Default length is two pin diameters on each side of the split

module LocatingPin(Dia=PinOD,Len=0.0) {

	PinLen = (Len != 0.0) ? Len : (4*Dia);

	translate([0,0,-ThreadThick])
		PolyCyl((Dia + 2*ThreadWidth),2*ThreadThick,4);

	translate([0,0,-2*ThreadThick])
		PolyCyl((Dia + 1*ThreadWidth),4*ThreadThick,4);

	translate([0,0,-(Len/2 + ThreadThick)])
		PolyCyl(Dia,(Len + 2*ThreadThick),4);

}

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

  Range = floor(50 / Space);

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

}

//----------------------
// The magazine block

module Block(SectionSelect = 0) {

CropHeight = Height*cos(Angle);				// block height perpendicular to base
echo(str("Perpendicular height: ",CropHeight));

	difference() {
		union() {
			intersection() {
				rotate([Angle,0,0])
					hull() {
						for (i=[-1,1])
							translate([0,i*RectLength/2,-((Length/2)*sin(Angle) + Protrusion)])
								cylinder(r1=SpringRadius,r2=(SpringRadius - Taper/2),
										 h=(Height + 2*(Length/2)*sin(Angle) + 2*Protrusion),
										 $fn=NumSides);
					}
				translate([0,0,CropHeight/2])
					cube([2*SpringID,3*Length,CropHeight],center=true);
			}
			translate([0,-Height*sin(Angle),Height*cos(Angle)])
				resize([(SpringID - Taper),0,0])
					intersection() {
						rotate([Angle,0,0])
							translate([0,0,-(HornAddBase + Protrusion)])
								cylinder(r1=HornBaseOD/2,
										r2=HornTipOD/2,
										h=(HornLength + HornAddLength + Protrusion),
										$fn=NumSides);
					cube([2*SpringID,Length,2*(HornLength*cos(Angle) + Protrusion)],center=true);
				}
		}

		translate([0,ScrewOffset,-Protrusion])		// screw
			rotate(180/6)
				PolyCyl(ScrewOD,(ScrewLength + Protrusion),6);

		translate([0,ScrewOffset,NutTrapBaseHeight])		// nut trap in center
			rotate(180/6)
				PolyCyl(NutOD,NutTrapLength,6);

		translate([0,ScrewOffset,-Protrusion])		// nut clearance at base
			rotate(180/6)
				PolyCyl(NutOD,(1.1*NutThick + Protrusion),6);

		translate([SpringID/2,CrimpOffset,-Protrusion])
			rotate(180)
				cube([SpringID,Length,(CrimpHeight + Protrusion)],center=false);

		if (AlignPins)								// alignment pins
			if (true)
				translate([0,-CropHeight*tan(Angle),CropHeight])
					rotate([0,90,0]) rotate(45 + Angle)
						LocatingPin(PinOD,PinLength);
			else
			for (i=[-1,1])			// cannot use these with additional vents * channels
				rotate([Angle,0,0])
				translate([0,
							(i*((Length/2)*cos(Angle) - PinInset)),
							(CropHeight/2 - i*2*PinInset)])
					rotate([0,90,0]) rotate(45 - Angle)
						LocatingPin(PinOD,PinLength);

		translate([0,(ScrewOffset + 1.25*NutOD),ScrewLength])	// air vent
			rotate([90,0,0]) rotate(180/8)
				PolyCyl(VentDia,3*NutOD,8);
		translate([0,VentOffset,-(VentDia/2)*tan(Angle)])
			rotate([Angle,0,0]) rotate(180/8)
				PolyCyl(VentDia,VentLength,8);

		translate([0,RecessOffset,0])			// weight reduction recess
			rotate([Angle,0,0]) rotate(180/8)
				translate([0,0,-((RecessDia/2)*tan(Angle))])
				PolyCyl(RecessDia,(RecessLength + (RecessDia/2)*tan(Angle)),8);

		if (SectionSelect == 1)
			translate([0*SpringID,-2*Length,-Protrusion])
				cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
		else if (SectionSelect == -1)
			translate([-2*SpringID,-2*Length,-Protrusion])
				cube([2*SpringID,4*Length,(Height + HornLength + 2*Protrusion)],center=false);
	}

SupportSlots = (SupportWidth / (4*ThreadWidth)) / 2;		// SupportWidth is multiple of 4*ThreadWidth

	if (Support)
	color("Yellow") {
		translate([0,(CrimpOffset - SupportLength/2),SupportThick/2])
			difference() {
				translate([0,-ThreadWidth,0])
					cube([(SupportWidth - Protrusion),SupportLength,SupportThick],center=true);
				for (i=[-SupportSlots:SupportSlots])
					translate([i*4*ThreadWidth + 0*ThreadWidth,ThreadWidth,0])
						cube([(2*ThreadWidth),SupportLength,(SupportThick + 2*Protrusion)],center=true);
			}

		translate([0,ScrewOffset,0])
			for (j=[0:5]) {
				rotate(30 + 360*j/6)
					translate([(NutOD/2 - ThreadWidth)/2,0,(1.1*NutThick - ThreadThick)/2])
						color("Yellow")
						cube([(NutOD/2 - ThreadWidth),
							(2*ThreadWidth),
							(1.1*NutThick - ThreadThick)],
							center=true);
			}

	}

}

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

ShowPegGrid();

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

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

if (Layout ==  "Split") {
	translate([(Offset + Length/2),Height/2,0])
		rotate(90) rotate([0,-90,-Angle])
			Block(-1);
	translate([-(Offset + Length/2),Height/2,0])
		rotate(-90) rotate([0,90,Angle])
			Block(1);
}

, ,

3 Comments

Sewing Machine Lights: LED Strip Mount Solid Models

Mary’s Sears Kenmore Model 158 sewing machine arm has a flat rear surface and a plastic plate on the front, so double-sided adhesive foam tape can hold a straight mount in place; we rejected putting strips under the arm to avoid snagging on the quilts as they pass by. So, with LEDs in hand, these are the mounts…

LED strip lights must have strain relief for their wires, as our Larval Engineer discovered the hard way on her longboard ground lighting project, and I wanted nice endcaps to avoid snagging on the fabric, so the general idea was a quarter-round rod with smooth endcaps and a hole to secure the wire. Some experiments showed that the acrylic (?) LED encapsulation directed the light downward, thus eliminating the need for a shade.

So, something like this will do for a first pass:

LED Strip Light Mount - bottom view

LED Strip Light Mount – bottom view

The overall dimensions for the LED mounts:

  • Length: N x 25 mm, plus endcap radii
  • Front-to-back width: 10 mm to allow for strip variation and 1 mm protection
  • Top-to-bottom height: 12 mm to fit double-sided foam sticky squares
  • Wire channels: 3 mm diameter or square cross-section

If there’s not enough light, I think a double-wide mount with two parallel LED strips would work.

After a bit of screwing around with additive endcaps that produced catastrophically non-manifold solid models, I figured out the proper subtractive way to build the mounts: the endcaps actually define the overall shape of the mount.

Start by placing a pair of spheroids, with radii matching the strip dimensions, so that their outer poles match the desired overall length:

Strip Light Mount - end cap spheroids - whole

Strip Light Mount – end cap spheroids – whole

The north/south poles must face outward, so that the equal-angle facets along the equators match up with what will become the mount body: rotate the spheroids 90° around the Y axis. The centers lie at the ends of the LED segments; the model shown here has a single 25 mm segment.

Then hack off three quadrants:

Strip Light Mount - end cap spheroids

Strip Light Mount – end cap spheroids

That leaves two orange-segment shapes that define the endcaps:

Strip Light Mount - end caps - shaped

Strip Light Mount – end caps – shaped

Here’s the key step that took me far too long to figure out. Shrinkwrapping the endcaps with the hull() function finesses the problem of matching the body facets to the endcap facets:

Strip Light Mount - end caps - hull

Strip Light Mount – end caps – hull

Model the wire channels as positive volumes that will be subtracted from the mount. The Channels layout shows both channels separated by a short distance:

Strip Light Mount - positive wire channels

Strip Light Mount – positive wire channels

The horizontal hexagons started as squares, but that looked hideous on the rounded endcaps.

Seen from the bottom, the mount starts like this:

Strip Light Mount - no wiring channels

Strip Light Mount – no wiring channels

Position and subtract a wire channel:

Strip Light Mount - visible wire channel

Strip Light Mount – visible wire channel

Which leaves the final solid model as a single, manifold object:

Strip Light Mount - complete

Strip Light Mount – complete

The module generating the mount takes three parameters: the number of LED segments and two string variables that determine whether to punch a channel in each endcap. Instantiate the module three times with suitable parameters to get a trio of LED mounts, all laid out for 3D printing:

Strip Light Mount - build layout

Strip Light Mount – build layout

They built just exactly like those models would suggest; the M2 produces dependable results.

The OpenSCAD source code:

// LED Strip Lighting Brackets for Kenmore Model 158 Sewing Machine
// Ed Nisley - KE4ZNU - February 2014

Layout = "Strip";			// Build Show Channels Strip

//- Extrusion parameters must match reality!
//  Print with 2 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

inch = 25.4;

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

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

Segment = [25.0,10.0,3.0];		//  size of each LED segment

WireChannel = 3.0;				// wire routing channel

StripHeight = 12.0;				// sticky tape width
StripSides = 8*4;

DefaultLayout = [1,"Wire","NoWire"];

EndCap = [(2*WireChannel + 1.0),Segment[1],StripHeight];	// radii of end cap spheroid
EndCapSides = StripSides;

CapSpace = 2.0;						// build spacing for endcaps
BuildSpace = 1.5*Segment[1];		// spacing between objects on platform

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

}

//-- The negative space used to thread wires into the endcap

module MakeWireChannel(Which = "Left") {

	HalfSpace = EndCap[0] * ((Which == "Left") ? 1 : -1);

	render(convexity=2)
	translate([0,EndCap[1]/3,0])
		intersection() {
			union() {
				cube([2*WireChannel,WireChannel,EndCap[2]],center=true);
				translate([-2*EndCap[0],0,EndCap[2]/2])
					rotate([0,90,0]) rotate(180/6)
						PolyCyl(WireChannel,4*EndCap[0],6);
			}
			translate([HalfSpace,0,(EndCap[2] - Protrusion)]) {
				cube(2*EndCap,center=true);
			}
		}
}

//-- The whole strip, minus wiring channels

module MakeStrip(Layout = DefaultLayout) {

	BarLength = Layout[0] * Segment[0];				// central bar length

	hull()
		difference() {
			for (x = [-1,1])						// endcaps as spheroids
				translate([x*BarLength/2,0,0])
					resize(2*EndCap) rotate([0,90,0]) sphere(1.0,$fn=EndCapSides);
			translate([0,0,-EndCap[2]])
				cube([2*BarLength,3*EndCap[1],2*EndCap[2]],center=true);
			translate([0,-EndCap[1],0])
				cube([2*BarLength,2*EndCap[1],3*EndCap[2]],center=true);
		}

}

//-- Cut wiring channels out of strip

module MakeMount(Layout = DefaultLayout) {

	BarLength = Layout[0] * Segment[0];

	difference() {
		MakeStrip(Layout);
		if (Layout[1] == "Wire")
			translate([BarLength/2,0,0])
				MakeWireChannel("Left");
		if (Layout[2] == "Wire")
			translate([-BarLength/2,0,0])
				MakeWireChannel("Right");
	}
}

//- Build it

ShowPegGrid();

if (Layout == "Channels") {
	translate([ EndCap[0],0,0]) MakeWireChannel("Left");
	translate([-EndCap[0],0,0]) MakeWireChannel("Right");
}

if (Layout == "Strip") {
	MakeStrip(DefaultLayout);
}

if (Layout == "Show") {
	MakeMount(DefaultLayout);
}

if (Layout == "Build") {

	translate([0,BuildSpace,0]) MakeMount([1,"Wire","Wire"]);		// rear left side, vertical
	translate([0,0,0]) MakeMount([5,"Wire","NoWire"]);				// rear top, across arm
	translate([0,-BuildSpace,0]) MakeMount([6,"NoWire","Wire"]);	// front top, across arm
}

The original design doodles, which bear a vague resemblance to the final mounts:

LED Strip Light Mounts - Original Design Sketches

LED Strip Light Mounts – Original Design Sketches

The little snood coming out of the top would hide a wire going through a hole drilled in the capital-S of “Sears” on the front panel, but I came to my senses long before implementing that idea…

, ,

2 Comments