The Smell of Molten Projects in the Morning

Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.

Tag: CNC

Making parts with mathematics

  • Victoreen 710-104 Ionization Chamber: Mounting Dimensions

    Mounting a circuit board atop the Victoreen 710-104 ionization chamber requires figuring out the location of those 6-32 studs:

    Victoreen 710-104 Ionization Chamber - oblique
    Victoreen 710-104 Ionization Chamber – oblique

    Given that it dates back to the early Cold War days, the bolt circle dimensions are all hard inch:

    Victoreen 710-104 Ionization Chamber - mounting dimensions
    Victoreen 710-104 Ionization Chamber – mounting dimensions

    I embossed the studs into a pad of Geek Scratch Paper, eyeballed the stud-to-stud spacing from a cheap ruler, back-calculated the BCD, rounded it from 2.742 to the obvious 2.75, then fed that into the first BCD calculator that appeared in the obvious search.

    The can is just over 3.5 inch OD and stands 1.5 inch tall.

    The can will run at +24 V in relation to the rest of the circuitry, so the studs must be insulated from the PCB’s copper pours. That, most likely, will require some 3D printed doodads.

    The circuitry must live inside a grounded metallic can that excludes random electric fields. Somewhere in the pile, I have a few sheets of Mu-metal that, while grossly overqualified for the task (even without heat treatment), should solder up nicely…

  • CNC Workshop 2015: Practical Solid Modeling with OpenSCAD

    HP Plotter Pen Polygon
    HP Plotter Pen Polygon

    This afternoon at the CNC Workshop, I’ll be bootstrapping folks into creating 3D-printable solid models with Openscad.

    The presentation in PDF form:

    Practical Solid Modeling for 3D Printing with OpenSCAD – CNC Workshop 2015

    The OpenSCAD source code for the exercises, in case you don’t want to type along:

    Practical Solid Modeling for 3D Printing with OpenSCAD – Models.zip.odt

    When you download that file, you’ll get something ending in .zip.odt. Rename it to remove the .odt extension, because it’s really a ZIP file; WordPress doesn’t allow users to uploads ZIP files.

  • HP7475A Plotter: Never Throw Anything Out

    Our Larval Engineer stopped by, on her way to a half-year co-op job out around Route 128, and devoted a few days to merge-sorting / triaging her possessions. Having shown her the HP 74754A plotter project, she later dropped a bag o’ stuff on my desk without comment:

    HP7475A - My old pens racks doodles
    HP7475A – My old pens racks doodles

    The perforated pen holder stuck to the plotter case (hey, it would still fit!) in front of the carousel with a bit of foam tape on an angled bracket you can’t quite see. It held 15 pens at the ready: I really used that plotter.

    The doodle on the yellow sheet sketches a bulky adapter between the spindle nose thread on the Sherline CNC mill and a plotter cartridge. The flange-less pen body might just fit into the spindle bore, but I remember concluding that machining pen bodies or adapters wasn’t worth the effort. Now it’s a simple matter of some OpenSCAD source code and a few hours of hands-off production, so perhaps I should re-think that.

    No dates on anything, but I got the Sherline in 2004. The pen holder probably dates back to the late 80s, shortly after I got the plotter. Most likely, I gave her the bag o’ stuff and told her to make something interesting; it could still happen…

     

  • Proto Board Holder: 80×110 mm Version

    A simple holder for 80×110 mm prototyping boards:

    Random LED Dots - circuit board
    Random LED Dots – circuit board

    It’s similar to the holder for the LED current controller board, minus the center screws, plus nicely rounded corners and a cutout for wires emerging from underneath:

    Proto board holder
    Proto board holder

    Slic3r’s Hilbert Curve infill definitely looks better than the usual straight-line pattern:

    Circuit Board Holder - Slic3r preview
    Circuit Board Holder – Slic3r preview

    The OpenSCAD source code:

    // Test support frame for Hall Effect LED Blinky Light
    // Ed Nisley KE4ZNU - Sept 2013
    
    Layout = "Fancy";				// Fancy Plain
    
    PlainColor = "LightBlue";
    
    ClampFlange = true;
    
    //- Extrusion parameters - must match reality!
    
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    Protrusion = 0.1;
    
    HoleWindage = 0.2;
    
    //- Screw sizes
    
    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;
    
    //- PCB sizes
    
    PCBSize = [110.0,80.0,1.5];
    PCBShelf = 2.0;
    
    Clearance = 2*[ThreadWidth,ThreadWidth,0];
    
    WallThick = IntegerMultiple(5.0,ThreadWidth);
    FrameHeight = 8.0;
    
    ScrewOffset = 0.0 + Clear4_40/2;
    
    OAHeight = FrameHeight + Clearance[2] + PCBSize[2];
    
    FlangeExtension = 5.0;
    FlangeThick = IntegerMultiple(2.0,ThreadThick);
    Flange = PCBSize
    			+ 2*[ScrewOffset,ScrewOffset,0]
    			+ 2*[Washer4_40OD,Washer4_40OD,0]
    			+ [2*FlangeExtension,2*FlangeExtension,(FlangeThick - PCBSize[2])]
    			;
    
    echo("Flange: ",Flange);
    NumSides = 4*5;
    
    WireChannel = [Flange[0],15.0,3.0 + PCBSize[2]];
    WireChannelOffset = [Flange[0]/2,25.0,( + FrameHeight + PCBSize[2] - WireChannel[2]/2)];
    
    //- Adjust hole diameter to make the size come out right
    
    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);
    }
    
    //- Put peg grid on build surface
    
    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 it
    
    ShowPegGrid();
    
    difference() {
    	union() {									// body block and screw bosses
    		translate([0,0,OAHeight/2])
    			color(PlainColor)
    			cube(PCBSize + Clearance + [2*WallThick,2*WallThick,FrameHeight],center=true);
    		for (x=[-1,1], y=[-1,1]) {
    			translate([x*(PCBSize[0]/2 + ScrewOffset),
    						y*(PCBSize[1]/2 + ScrewOffset),
    						0])
    				color((Layout == "Fancy") ? "Orchid" : PlainColor)
    				cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    		}
    		if (ClampFlange)
    			color((Layout == "Fancy") ? "SeaGreen" : PlainColor)
    			linear_extrude(height=Flange[2])
    				hull()
    					for (i=[-1,1], j=[-1,1]) {
    						translate([i*(Flange[0]/2 - Washer4_40OD/2),j*(Flange[1]/2 - Washer4_40OD/2)])
    							circle(d=Washer4_40OD,$fn=NumSides);
    					}
    	}
    
    	for (x=[-1,1], y=[-1,1]) {				// screw holes and washer recesses
    		translate([x*(PCBSize[0]/2 + ScrewOffset),
    					y*(PCBSize[1]/2 + ScrewOffset),
    					-Protrusion])
    			rotate((x-1)*90)
    			PolyCyl(Tap4_40,(OAHeight + 2*Protrusion));
    		translate([x*(PCBSize[0]/2 + ScrewOffset),
    					y*(PCBSize[1]/2 + ScrewOffset),
    					OAHeight - PCBSize[2]])
    			PolyCyl(1.2*Washer4_40OD,(PCBSize[2] + Protrusion),NumSides);
    	}
    
    	translate([0,0,OAHeight/2])					// through hole below PCB
    		cube(PCBSize - 2*[PCBShelf,PCBShelf,0] + [0,0,2*OAHeight],center=true);
    
    	translate([0,0,(OAHeight - (PCBSize[2] + Clearance[2])/2 + Protrusion/2)])	// PCB pocket on top
    		cube(PCBSize + Clearance + [0,0,Protrusion],center=true);
    
    	translate(WireChannelOffset)									// clearance for cable on solder side
    		cube(WireChannel + [0,0,Protrusion],center=true);
    }
    
  • HP 7475A Plotter: Roland Knife Stabilizer, Improved

    The objective being to wrap a nose around the cutter blade to allow some control over the cut depth, I lengthened the cylinder around the cutter body and modeled a discrete glue-on cap:

    Roland knife stabilizer and nose - show
    Roland knife stabilizer and nose – show

    Which, with an additional 80 g of ballast, worked fine in the double-thick vinyl:

    Roland knife stabilizer - nut weight
    Roland knife stabilizer – nut weight

    The pen-lift spring can just barely manage to heave that load off the vinyl, but it’s obviously running at the limit of its ability and this can’t possibly be a Good Thing for the mechanism in the long run.

    After a bit more fiddling around, I noticed that the stabilizer wasn’t sitting flat on the pen holder and that there really wasn’t any good reason to have a separate cap, so I did one more revision:

    Roland knife stabilizer with nose - side view
    Roland knife stabilizer with nose – side view

    The cutaway view shows the knife model now has tapered transition from the body to the grossly enlarged blade, so the model will build without supports inside the cylinder.

    A little cutout on one wall lets the plate sit flat on the pen holder and a barely visible recess in the cylinder gives the carousel pen-capping actuator a bit more clearance:

    Roland knife stabilizer with nose - Slic3r preview
    Roland knife stabilizer with nose – Slic3r preview

    It works about as well as the version shown above, minus the tedious gluing, so I’ll call it a success… even though it’s obviously not going to get much use. I don’t see any way to apply enough downforce to make the cutter work; the mechanical changes just aren’t worthwhile.

    The OpenSCAD source code, which includes some tweaks and outright kludges since the first version, builds adapters for Sakura pens (which work just fine) as well as this knife stabilizer:

    // HP 7475A plotter pen adapters
    // Ed Nisley KE4ZNU April 2015
    
    Layout = "BuildStabilizer";
    							// ShowBody BuildBody BodyPoly
    							// ShowPen ShowPenAdapter BuildPenAdapter Plug Pen PenPoly
    							// ShowKnife BuildKnife KnifeAdapter Knife
    							// ShowStabilizer Stabilizer BuildStabilizer
    
    //- Extrusion parameters must match reality!
    
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;
    
    Protrusion = 0.1;			// make holes end cleanly
    
    inch = 25.4;
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    //----------------------
    // Dimensions
    // Z=0 at pen tip!
    
    NumSides = 8*4;						// number of sides on each "cylinder"
    
    RADIUS = 0;							// subscript for radius values
    HEIGHT = 1;							//   ... height above Z=0
    
    //-- Original HP plotter pen, which now serves as a body for the actual pen
    
    BodyOutline = [						// X values = (measured diameter)/2, Y as distance from tip
    	[0.0,0.0],						//  0 fiber pen tip
    //	[2.0/2,1.4],					//  1 ... taper (not buildable)
    	[1.0/2,0.005],					//  1 ... faked point to remove taper
    	[2.0/2,0.0],[2.0/2,2.7],		//  2 ... cylinder
    	[3.7/2,2.7],[3.7/2,4.45],		//  4 tip surround
    	[4.8/2,5.2],					//  6 chamfer
    	[6.5/2,11.4],					//  7 rubber seal face
    	[8.9/2,11.4],					//  8 cap seat
    	[11.2/2,15.9],					//  9 taper to body
    	[11.5/2,28.0],					// 10 lower body
    	[13.2/2,28.0],[16.6/2,28.5],	// 11 lower flange = 0.5
    	[16.6/2,29.5],[13.2/2,30.0],	// 13 flange rim = 1.0
    	[11.5/2,30.0],					// 15 upper flange = 0.5
    	[11.5/2,43.25],					// 16 upper body
    	[0.0,43.25]						// 17 lid over reservoir
    	];
    
    TrimHeight = BodyOutline[9][HEIGHT];		// cut off at top of lower taper
    SplitHeight = (BodyOutline[11][HEIGHT] + BodyOutline[14][HEIGHT])/2;	// middle of flange
    
    FlangeOD = 2*BodyOutline[13][RADIUS];
    FlangeTop = BodyOutline[15][HEIGHT];
    
    BodyOD = 2*BodyOutline[16][RADIUS];
    BodyOAL = BodyOutline[17][HEIGHT];
    
    echo(str("Trim: ",TrimHeight));
    echo(str("Split: ",SplitHeight));
    
    BuildSpace = FlangeOD;
    
    //-- Sakura Micron fiber-point pen
    
    ExpRP = 0.15;						// expand critical sections (by radius)
    
    //-- pen locates in holder against end of outer body
    
    PenOutline = [
    	[0,0],							//  0 fiber pen tip
    	[0.6/2,0.0],[0.6/2,0.9],		//  1  ... cylinder
    	[1.5/2,0.9],[1.5/2,5.3],		//  3 tip surround
    	[4.7/2,5.8],					//  5 chamfer
    	[4.9/2,12.3],					//  6 nose
    //	[8.0/2,12.3],[8.0/2,13.1],		//  7 latch ring
    //	[8.05/2,13.1],[8.25/2,30.5],	//  9 actual inner body
    	[8.4/2 + ExpRP,12.3],[8.4/2 + ExpRP,30.5],	//  7 inner body - clear latch ring
    	[9.5/2 + ExpRP,30.5],			//  9 outer body - location surface!
    	[9.8/2 + ExpRP,50.0],			// 10 outer body - length > Body
    	[7.5/2,50.0],					// 11 arbitrary length
    	[7.5/2,49.0],					// 12 end of reservoir
    	[0,49.0]						// 13 fake reservoir
    	];
    
    PenNose = PenOutline[6];
    PenLatch = PenOutline[7];
    
    PenOAL = PenOutline[11][HEIGHT];
    
    //-- Plug for end of cut-off pen body
    //   you need two plugs...
    
    PlugOutline = [
    	[0,0],							// 0 center of lid
    	[9.5/2,0.0],[9.5/2,1.0],		// 1 lid rim <= body OD
    	[7.9/2,1.0],					// 3 against end of pen
    	[7.6/2,6.0],					// 4 taper inside pen body
    	[5.3/2,6.0],					// 5 against ink reservoir
    	[4.0/2,1.0],					// 6 taper to lid
    	[0.0,1.0]						// 7 flat end of taper
    	];
    
    PlugOAL = PlugOutline[5][HEIGHT];
    
    //   cap locates against end of inner body at latch ring
    //-- cap origin is below surface to let pen tip be at Z=0
    
    CapGap = 1.0;						// gap to adapter body when attached
    CapGripHeight = 2.0;				// thickness of cap grip flange
    CapTipClearance = 1.0;				// clearance under fiber tip
    
    CapOffset = -(CapGripHeight + CapTipClearance);	// align inside at pen tip Z=0
    
    CapOutline = [
    	[0,CapOffset],									// 0 base
    	[FlangeOD/2,CapOffset],							// 1 finger grip flange
    	[FlangeOD/2,CapOffset + CapGripHeight],			// 2  ... top
    	[BodyOD/2,CapOffset + CapGripHeight],			// 3 shaft
    	[BodyOD/2,TrimHeight - CapGap],					// 4  ... top with clearance
    	[PenLatch[RADIUS],TrimHeight - CapGap],			// 5 around pen latch ring
    	[PenLatch[RADIUS],PenNose[HEIGHT]],				// 6  ... location surface!
    	[PenNose[RADIUS] + ExpRP,PenNose[HEIGHT]],		// 7 snug around  nose
    	[PenNose[RADIUS] + ExpRP,-CapTipClearance],		// 8 clearance around tip
    	[0,-CapTipClearance],							// 9  ... bottom
    	];
    
    //-- Roland drag knife bearing assembly
    
    ExpRK = 0.30;						// expand critical sections (by radius)
    AdjLen = 2.0;						// allowance for adjustment travel
    
    //- Knife tweaked for pen adapter
    /*
    KnifeOutline = [
    	[0,0],							//  0 blade point (actually 0.25 mm offset)
    	[1.0/2,0.0],					//  1  ... blunt end
    	[1.0/2,4.0],					//  2  ... cylinder
    	[2.0/2,4.0],					//  3 blade shank
    	[2.0/2,5.9],					//  4  .. at bearing
    	[6.0/2,5.9],					//  5 holder - shell
    
    	[7.3/2 + ExpRK,8.3],			//  6 holder - taper to body
    	[7.3/2 + ExpRK,21.0 - AdjLen],	//  7 holder body
    
    	[8.8/2 + ExpRK,22.0 - AdjLen],	//  8 holder - threads bottom
    	[8.8/2 + ExpRK,25.0],[9.0/2 + ExpRK,26.0],		//  9 clear threads to reduce friction
    	[9.0/2 + ExpRK,32.0],[8.8/2 + ExpRK,33.0],		// 11  ... end clearance
    	[8.8/2 + ExpRK,42.5 - AdjLen],	// 13 holder - threads top = locknut bottom
    	[12.5/2,42.5 - AdjLen],			// 14 knurled locknut - adjustment travel
    	[12.5/2,45.8],					// 15 knurled locknut - top
    	[11.0/2,45.8],					// 16 holder - adjusting knurl
    	[11.0/2,52.0],					// 17 holder - top surface
    	[3.0/2,52.0],[3.0/2,57.2],		// 18 spring post
    	[0.0,57.2]						// 19 end of post
    	];
    */
    
    //- Knife tweaked for stabilizer
    
    KnifeOutline = [
    	[0,0],							//  0 blade point (actually 0.25 mm offset)
    	[3.0/2,0.0],					//  1  ... blunt end
    	[3.0/2,4.0],					//  2  ... cylinder
    	[3.0/2,4.0],					//  3 blade shank
    	[6.0/2,5.9],					//  4  .. at bearing (taper to support nose)
    	[6.0/2,5.9],					//  5 holder - shell
    
    	[7.3/2 + ExpRK,8.3],			//  6 holder - taper to clear threads
    	[7.3/2 + ExpRK,21.0 - AdjLen],	//  7  ..
    
    	[8.8/2 + ExpRK,22.0 - AdjLen],	//  8 holder - threads bottom
    	[8.8/2 + ExpRK,25.0],[9.0/2 + ExpRK,26.0],		//  9 clear threads to reduce friction
    	[9.0/2 + ExpRK,32.0],[8.8/2 + ExpRK,33.0],		// 11  ... end clearance
    
    	[8.8/2 + ExpRK,42.5 - AdjLen],	// 13 holder - threads top = locknut bottom
    	[12.5/2,42.5 - AdjLen],			// 14 knurled locknut - adjustment travel
    	[12.5/2,45.8],					// 15 knurled locknut - top
    	[11.0/2,45.8],					// 16 holder - adjusting knurl
    	[11.0/2,52.0],					// 17 holder - top surface
    	[3.0/2,52.0],[3.0/2,57.2],		// 18 spring post
    	[0.0,57.2]						// 19 end of post
    	];
    
    ThreadStart = KnifeOutline[8][HEIGHT];
    ThreadOD = 2*KnifeOutline[11][RADIUS];
    
    //-- Plotter pen holder stabilizer
    
    HolderPlateThick = 3.0;				// thickness of plate atop holder
    RimHeight = 5.0;					// rim around sides of holder
    RimThick = 2.0;						// wall thickness
    
    HolderOrigin = [17.0,12.2,0.0];		// center of pen tip relative to polygon coordinates
    
    HolderHeight = 30.0;				// top of holder to platen in pen-down position
    HolderTopThick = 1.7;				// top of holder to top of pen flange
    
    HolderNoseLength = 4.0;				// length of nose taper
    HolderKnifeOffset = -3.0;			// additional downward adjustment range
    
    LockScrewInset = 3.0;				// from right edge of holder plate
    LockScrewOD = 2.0;					// tap for 2.5 mm screw
    
    UncapperHeight = -17.0;				// uncapping actuator arm from top of pen holder
    UncapperOD = 11.0;					//  ... max OD that clears uncapper
    
    // Beware: update hardcoded subscripts in Stabilizer() when adding / deleting point entries 
    
    HolderPlate = [
    	[8.6,18.2],[8.6,23.9],			// 0 lower left corner of pen recess
    	[13.9,23.9],[13.9,30.0],		// 2
    //	[15.5,30.0],[15.5,25.0],		// 4 omit middle of support beam
    //	[20.4,25.0],[20.4,30.0],		// 6
    	[22.7,30.0],[22.7,27.5],		// 4
    	[35.8,27.5],[35.8,20.7],		// 6 spring box corner
    	[43.0,20.7],					// 8
    	[31.5,0.0],						// 9
    //	[24.5,0.0],[24.5,8.0],			// 10 omit pocket above pen clamp
    //	[22.5,10.0],[22.5,16.5],		// 12
    //	[20.5,18.2]						// 14
    	[13.6,0.0],						// 10
    	[8.6,5.0]						// 11
    	];
    
    BeamWidth = HolderPlate[4][0] - HolderPlate[2][0];		// rear support beam
    
    TabWidth = HolderPlate[1][1] - HolderPlate[0][1];		// tab extending left beyond pen recess
    TabClear = 3.0;											// maximum rim height over tab
    
    HolderCylinderOutline = [
    	[0,0],											//  0 center of nose
    	[6.0/2,0.0],									//  1 flat nose surface OD
    	[BodyOD/2,HolderNoseLength],			//  2 taper to cylinder OD
    	[BodyOD/2,HolderHeight],				//  3 cylinder to top of holder plate
    	[0,HolderHeight]								//  4 flat top
    	];
    
    //----------------------
    // 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);
    }
    
    //- Locating pin hole with glue recess
    //  Default length is two pin diameters on each side of the split
    
    PinOD = 1.75;
    PinOC = BodyOD / 2;
    
    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 LocatingPins(Length) {
    	for (i=[-1,1])
    	translate([0,i*PinOC/2,0])
    		rotate(180/4)
    		LocatingPin(Len=Length);
    }
    
    //----------------------
    // Basic shapes
    
    //-- HP plotter pen body
    
    module ShowPolygon(pts) {
    	polygon(pts);
    }
    
    module Body() {
    	render(convexity=3)
    		rotate_extrude($fn=NumSides)
    			polygon(points=BodyOutline);
    }
    
    //-- Sakura drawing pen body
    
    module Pen() {
    	rotate_extrude($fn=NumSides)
    		polygon(points=PenOutline);
    }
    
    //-- Plug for top of Sakura pen
    
    module Plug() {
    	render(convexity = 2)
    		rotate_extrude($fn=NumSides)
    			polygon(points=PlugOutline);
    }
    
    //-- Cap for tip of Sakura pen
    
    module Cap() {
    	render(convexity = 2)
    		rotate_extrude($fn=NumSides)
    			polygon(points=CapOutline);
    }
    
    //-- Sakura pen adapter
    
    module PenAdapter() {
    
    	render(convexity=3)
    		difference() {
    			Body();
    			Pen();
    			translate([0,0,TrimHeight/2])
    				cube([2*FlangeOD,2*FlangeOD,TrimHeight],center=true);
    		}
    }
    
    //-- Roland knife body
    
    module Knife() {
    	render(convexity=3)
    		rotate_extrude($fn=NumSides)
    			polygon(points=KnifeOutline);
    }
    
    //-- Roland knife adapter
    
    module KnifeAdapter(TrimZ = false) {
    
    Trans = TrimZ ? - TrimHeight : 0;
    
    	render(convexity=5)
    		translate([0,0,Trans])
    			difference() {
    				Body();
    				Knife();
    				translate([0,0,TrimHeight/2])
    					cube([2*FlangeOD,2*FlangeOD,TrimHeight],center=true);
    			}
    }
    
    //-- nose cap for stabilizer cylinder
    
    module StabilizerNose() {
    	render(convexity = 2)
    		rotate_extrude($fn=NumSides)
    			polygon(points=StabilizerNoseOutline);
    }
    
    //-- Roland knife stabilizer atop pen holder
    //   the trim blocks have offsets with magic numbers from the HolderPlate outline &c
    
    module Stabilizer(SeeKnife = false) {
    
    Cutout = (Layout == "ShowStabilizer") ? 0 : 1;
    
    	difference() {
    		union() {
    			translate(-HolderOrigin)													// put center of pen at origin
    			translate([0,0,-RimHeight])													// put top of holder at Z=0
    				difference() {
    						render(convexity=4)
    						linear_extrude(height=(HolderPlateThick + RimHeight))			// overall flange around edges
    							offset(r=RimThick)
    									polygon(points=HolderPlate);
    
    						render(convexity=4)
    						translate([0,0,-Protrusion])									// recess for pen holder plate
    							linear_extrude(height=(RimHeight + Protrusion))
    								polygon(points=HolderPlate);
    
    						translate([HolderPlate[7][0] - Protrusion,HolderPlate[7][1] - Protrusion,-Protrusion])	// trim spring box from top plate
    							cube([30,20,(RimHeight + HolderPlateThick + 2*Protrusion)]);
    
    						translate([27.0,HolderPlate[6][1] - Protrusion,-Protrusion])	// trim pivot plate clearance
    							cube([30,20,(RimHeight + HolderPlateThick + 2*Protrusion)]);
    
    						translate([HolderPlate[2][0],20,-Protrusion])					// trim left support beam
    							cube([BeamWidth,20,(RimHeight + Protrusion)]);
    
    						translate([0,HolderPlate[0][1],-(TabClear + Protrusion)])		// trim tab behind pen recess
    							cube([(HolderPlate[0][0] + Protrusion),TabWidth,RimHeight + Protrusion]);
    
    						translate([HolderPlate[9][0] - LockScrewInset,RimThick,RimHeight - HolderTopThick - LockScrewOD/2])												// lock screw on front edge
    							rotate([90,0,0])
    								rotate(180/4)
    									PolyCyl(LockScrewOD,3*RimThick);					// punch out hold-down screw hole
    				}
    
    			difference() {
    				translate([0,0,-HolderHeight])											// cylinder and nose
    					rotate_extrude($fn=NumSides)
    						polygon(points=HolderCylinderOutline);
    				translate([-HolderOrigin[0],-(BodyOD + Cutout*UncapperOD/2),(UncapperHeight - HolderHeight)])	// uncapper clearance
    					cube([2*HolderOrigin[0],BodyOD,HolderHeight]);
    			}
    		}
    
    		translate([0,0,-HolderHeight + HolderKnifeOffset])
    			if (SeeKnife)
    #				Knife();
    			else
    				Knife();
    
    	}
    }
    
    //----------------------
    // Build it
    
    if (Layout == "Pen")
    	Pen();
    
    if (Layout == "Knife")
    	Knife();
    
    if (Layout == "Stabilizer")
    	Stabilizer();
    
    if (Layout == "ShowBody")
    	Body();
    
    if (Layout == "BodyPoly") {
    	ShowPolygon(BodyOutline);
    	Body();
    }
    
    if (Layout == "PenPoly") {
    	ShowPolygon(PenOutline);
    	Pen();
    }
    
    if (Layout == "BuildBody") {
    	Spacing = 0.75*BuildSpace;
    	difference() {
    		union() {
    			translate([Spacing,0,-SplitHeight])
    				Body();
    			rotate([180,0,0])
    				translate([-Spacing,0,-SplitHeight])
    					Body();
    		}
    		translate([0,0,-BodyOAL])
    			cube(2*BodyOAL,center=true);
    		for (i = [-1,1])
    			translate([i*Spacing,0,0])
    				LocatingPins(5.0);
    	}
    }
    
    if (Layout == "Plug")
    	Plug();
    
    if (Layout == "KnifeAdapter")
    	KnifeAdapter();
    
    if (Layout == "ShowPen") {
    	color("AntiqueWhite") {
    		Pen();
    		translate([-1.5*BodyOD,0,0])
    			Pen();
    	}
    	color("Magenta",0.35) {
    		translate([0,0,PlugOAL + PenOAL + 3.0])
    			rotate([180,0,0])
    				Plug();
    		PenAdapter();
    		Cap();
    	}
    	color("Magenta") {
    		translate([1.5*BodyOD,0,PlugOAL + PenOAL + 3.0])
    			rotate([180,0,0])
    				Plug();
    		translate([1.5*BodyOD,0,0]) {
    			PenAdapter();
    			Cap();
    		}
    	}
    }
    
    if (Layout == "ShowPenAdapter") {
    	color("AntiqueWhite") {
    		translate([0.00*BodyOD,0,0])
    			Pen();
    		translate([-2.75*BodyOD,0,0])
    			Pen();
    	}
    
    	translate([-1.50*BodyOD,0,0])
    		color("SandyBrown")
    			Body();
    
    	translate([0.00*BodyOD,0,0])
    		color("SandyBrown",0.35)
    			PenAdapter();
    
    	translate([3.00*BodyOD,0,0])
    		color("SandyBrown")
    			PenAdapter();
    
    	translate([1.50*BodyOD,0,0])
    		difference() {
    			color("SandyBrown")
    				PenAdapter();
    			translate([-BodyOD,-2*BodyOD,0])
    				cube([2*BodyOD,2*BodyOD,PenOAL]);
    		}
    
    }
    
    if (Layout == "ShowKnife") {
    
    	color("Goldenrod") {
    		Knife();
    		translate([-1.5*BodyOD,0,0])
    			Knife();
    	}
    	color("Magenta",0.35)
    		KnifeAdapter();
    	color("Magenta") {
    		translate([1.5*BodyOD,0,0])
    			KnifeAdapter();
    	}
    
    }
    
    if (Layout == "BuildPenAdapter") {
    
    	if (false) {
    		for (j = [-1,1])
    			translate([j*BuildSpace/2,-0.7*BuildSpace,0])
    			Plug();
    
    		translate([0,0,-CapOffset])
    			Cap();
    	}
    	else {
    		Plug();
    	}
    
    	difference() {
    		union() {
    			translate([1.20*BuildSpace,0,-SplitHeight])
    				PenAdapter();
    			rotate([180,0,0])
    				translate([-1.20*BuildSpace,0,-SplitHeight])
    					PenAdapter();
    		}
    		translate([0,0,-BodyOAL])
    			cube(2*BodyOAL,center=true);
    	}
    
    }
    
    if (Layout == "BuildKnife") {
    
    	difference() {
    		union() {
    			translate([0.7*BuildSpace,0,-SplitHeight])
    				KnifeAdapter(false);
    			rotate([180,0,0])
    				translate([-0.7*BuildSpace,0,-SplitHeight])
    					KnifeAdapter(false);
    		}
    		translate([0,0,-BodyOAL])
    			cube(2*BodyOAL,center=true);
    	}
    
    }
    
    if (Layout == "BuildStabilizer") {
    
    	translate([0,0,HolderPlateThick])
    		rotate([0,180,0])
    			Stabilizer(false);
    }
    
    if (Layout == "ShowStabilizer") {
    
    	translate([BuildSpace/2,0,HolderHeight])
    			Stabilizer(true);
    	translate([-BuildSpace/2,0,HolderKnifeOffset])
    		Knife();
    }
    
  • HP 7475A Plotter: Vinyl Cutting, Poorly

    Well, with a bit of ballast, that little unsupported blade actually did cut some vinyl:

    HP 7475A knife stabilizer - big nut weight
    HP 7475A knife stabilizer – big nut weight

    That ugly nut adds 125 g to the pen holder’s 19±10 g (that’s the spec) down force and grossly overloads the pen-lift spring at the solenoid, but it did provide enough force to carve that little strip from the vinyl sheet. Vertical compliance comes from the pen holder’s travel, so the weight exerts a constant force at the knife tip.

    I’m using the front-panel controls to drive the pen around at whatever speed the FAST button provides, which limits the results to straight lines.

    The vinyl, leftover from a discarded window decoration, is 0.15 mm = 6 mil thick and definitely not intended for use with a cutter; vinyl cutter sheets and rolls seem to run around 0.07 mm = 3 mil.

    Obviously, this doesn’t have much potential, but it actually worked better than I expected. The unguarded blade bites into the substrate (which I did expect), so the next step is to wrap a nose around the blade for controlled depth-of-cut.

  • HP 7475A Plotter: Roland Knife Holder-Stabilizer

    Somewhat encouraged by the results of the height-map cap atop the plotter’s pen holder, I figured a unified knife adapter and stabilizer cap would work even better. That requires enough accuracy to build a real solid model, rather than just sketch a height map…

    Print out the the grid-overlaid image of the pen holder, then doodle coordinates & measurements all over the poor thing:

    HP 7475A Pen Holder - gridded doodle
    HP 7475A Pen Holder – gridded doodle

    Now I can toss that piece of paper…

    That, plus a bit of digital caliper work, produces a flurry of dimensions & an array of vertices:

    //-- Plotter pen holder stabilizer
    
    HolderPlateThick = 3.0;				// thickness of plate atop holder
    RimHeight = 5.0;					// rim around sides of holder
    RimThick = 2.0;
    
    HolderOrigin = [17.0,12.2,0.0];		// center of pen relative to polygon coordinates
    
    HolderZOffset = 30.0;				// top of holder in pen-down position
    HolderTopThick = 1.7;				// top of holder to top of pen flange
    HolderCylinderLength = 17.0;		// length of pen support structure
    
    HolderKnifeOffset = -2.0;			// additional downward adjustment range (not below top surface)
    
    LockScrewInset = 3.0;				// from right edge of holder plate
    LockScrewOD = 2.0;					// tap for 2.5 mm screw
    
    // Beware: update hardcoded subscripts in Stabilizer() when adding / deleting point entries 
    
    HolderPlate = [
    	[8.6,18.2],[8.6,23.9],			// 0 lower left corner of pen recess
    	[13.9,23.9],[13.9,30.0],		// 2
    //	[15.5,30.0],[15.5,25.0],		// 4 omit middle of support beam
    //	[20.4,25.0],[20.4,30.0],		// 6
    	[22.7,30.0],[22.7,27.5],		// 4
    	[35.8,27.5],[35.8,20.7],		// 6 spring box corner
    	[43.0,20.7],					// 8
    	[31.5,0.0],						// 9
    //	[24.5,0.0],[24.5,8.0],			// 10 omit pocket above pen clamp
    //	[22.5,10.0],[22.5,16.5],		// 12
    //	[20.5,18.2]						// 14
    	[13.6,0.0],						// 10
    	[8.6,5.0]						// 11
    	];
    
    BeamWidth = HolderPlate[4][0] - HolderPlate[2][0];
    

    The general idea is to extrude the overall shape of the stabilizer cap, carve out chunks to fit it onto the pen holder, then add a cylinder around the knife bearing:

    HP7475A - Roland knife stabilizer - bottom - thrown together view
    HP7475A – Roland knife stabilizer – bottom – thrown together view

    The OpenSCAD source code contains a bunch of magic numbers and indexes that pull values from the vertex array:

    module Stabilizer(SeeKnife = false) {
    
      difference() {
        union() {
          translate(-HolderOrigin)                                            // put center of pen at origin
            difference() {
                render(convexity=4)
                linear_extrude(height=(HolderPlateThick + RimHeight))         // overall flange around edges
                  offset(r=RimThick)
                      polygon(points=HolderPlate);
    
                render(convexity=4)
                translate([0,0,-Protrusion])                                  // recess for pen holder plate
                  linear_extrude(height=(RimHeight + Protrusion))
                    polygon(points=HolderPlate);
    
                translate([HolderPlate[7][0] - Protrusion,HolderPlate[7][1] - Protrusion,-Protrusion])  // trim spring box from top plate
                  cube([30,20,(RimHeight + HolderPlateThick + 2*Protrusion)]);
    
                translate([27.0,HolderPlate[6][1] - Protrusion,-Protrusion])  // trim pivot plate clearance
                  cube([30,20,(RimHeight + HolderPlateThick + 2*Protrusion)]);
    
                translate([HolderPlate[2][0],20,-Protrusion])                 // trim left support beam
                  cube([BeamWidth,20,(RimHeight + Protrusion)]);
    
                translate([HolderPlate[9][0] - LockScrewInset,RimThick,RimHeight - HolderTopThick - LockScrewOD/2])                        // lock screw on front edge
                  rotate([90,0,0])
                    rotate(180/4)
                      PolyCyl(LockScrewOD,3*RimThick);                        // hold-down screw hole
            }
    
          translate([0,0,(RimHeight - HolderCylinderLength + Protrusion)])
            cylinder(d=BodyOD,h=HolderCylinderLength + Protrusion,$fn=NumSides);  // surround knife threads
        }
    
        translate([0,0,-HolderZOffset + HolderKnifeOffset])
          if (SeeKnife)
    #        Knife();
          else
            Knife();
      }
    }
    

    A bottom view shows all the cutouts:

    HP7475A - Roland knife stabilizer - build layout
    HP7475A – Roland knife stabilizer – build layout

    The little hole in the front fits a screw that will pass under the top plate of the pen holder and prevent the cutting forces from pushing it off.

    As with the Sakura pen adapter, the knife point sits at (0,0,0) with the stabilizer cap positioned at the (estimated) top of the pen holder:

    Roland knife stabilizer - show layout
    Roland knife stabilizer – show layout

    After a few print-and-try iterations to align all the fiddly cutouts:

    HP 7475A - Roland knife stabilizer - installed
    HP 7475A – Roland knife stabilizer – installed

    The slope-topped block protruding toward you from the bottom right actuates the carousel’s pen capping mechanism; it doesn’t quite touch the side of an HP pen and is well clear of the knife holder.

    Because there’s still no depth control surrounding the knife blade, this won’t work well at all, but it should suffice for better measurements.

    The full source code is back at the beginning.