Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
Tag: Improvements
Making the world a better place, one piece at a time
Knockoff Roland drag knife blades and holders being cheap and readily available on eBay, it didn’t take long to figure out that they’re not drop-in replacements for HP pens:
HP 7475A – Roland knife holder vs HP pen
The Roland Cutter Knowledge PDF shows that the blade must protrude just slightly beyond the holder shell, letting the flat end stabilize the media and regulate the cut depth, but some experimentation was in order just to get the mechanics worked out.
The central brass blade holder looks like it should fit neatly inside the pen body outline:
HP 7475A – Roland knife holder – internal
A small O-ring normally fits in the thread gap to provide some friction between the two metal parts, with the knurled nut locking them together at the desired setting.
The blade rides on a smooth bearing pushed upward against a stop by a spring exerting 220-400 g on that rounded shaft. I think a real vinyl cutter would have a spring-loaded pin pushing downward on that shaft to provide vertical compliance at the blade tip, but I’ve never seen such a thing in real life.
That suggests half a pound of downward cutter force that the HP pen holder definitely can’t provide; the spec is 19±10 g.
Applying a digital caliper to the blade holder produced the usual measurement array:
//-- Drag knife holder
ExpRK = 0.30; // expand critical sections (by radius)
AdjLen = 2.0; // allowance for adjustment travel
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 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
];
ThreadLength = KnifeOutline[13][HEIGHT] - KnifeOutline[8][HEIGHT];
Which spins up into a solid model of the brass part:
HP7475A – Roland knife holder – solid model
The large ring is slightly larger than the actual knurled nut, to ensure it cuts off the top of the HP pen body.
The raised section in the middle of the threads provides a little relief, as screwing the holder into a sufficiently snug plastic sleeve turned out to require more effort than seemed reasonable. I don’t have a tap for what might be a loose 9×0.75 mm fine-pitch thread (the actual OD is 8.75), so it’s gotta form its own path.
Running the plotter in Etch A Sketch mode, that little blade actually cut a sheet of paper:
HP 7475A – Roland knife adapter – first cut
However, it didn’t cut very well at all, mostly because the pen holder doesn’t grip the adapter tightly enough to resist the lateral forces required to drive the blade through the paper, nor does it provide enough downward force to maintain the cut; I cheated by pressing on the holder to encourage the blade to keep on cutting.
By design, the plotter pen lift / drop mechanism doesn’t (and really can’t) apply enough downward force. A sliding bar across the entire width of the plotter raises the holder through a mechanical tab and lowers the holder by releasing the tab. A small spring then provides all the downward force, overcoming a dashpot that slows the pen drop to prevent crushing the nib against the paper.
Just for fun, though, I figured I should see what happens with the blade firmly anchored in the pen holder…
The HP 7475A plotter comes with a transparent smoke-brown plastic flip-up lid covering the carousel and pen holder, presumably to keep dust and fingers out of the moving parts. That lid also has has the side effect of limiting the pen length, presumably because HP didn’t want the 7475A to eat into their large-format plotter market. In any event, removing the lid leaves another barrier to longer pens: the rugged plastic case between the carousel and the pen holder.
That’s from an earlier test, before I sawed the slot in the case, with all the machinery behind the pen holder in full view.
The test plot, with the proper pen colors and widths loaded in the carousel, looks pretty good:
HP7475A – Sakura Micro Pens – self-test plot
The pen holder wasn’t intended to support a long pen, so that shaft tends to torque the pen tip out of position, particularly while drawing characters:
HP 7475A – long black pen – misalignment
The various pen tips don’t all point to the same place:
HP 7475A – long RGBK pen misalignment
That could be non-concentric pen adapters, misalignment in the pen holder, or slightly off-center pen nibs. The offsets between the colors remains consistent in all the bar-chart columns, so the pen adapters aren’t shifting in the holder.
The worst-case error between bar-chart rectangles amounts to 0.5 mm parallel to the pen holder motion and 0.8 mm parallel to the paper motion. In round numbers, the pen tip is 30 mm from the flange, so moving it 0.5 mm to the side tips the pen 1°. The flange is 17 mm OD, which means a 1° tilt raises one edge by 0.3 mm or both edges by ±0.15 mm. Given a 0.25 mm 3D printed thread thickness, that’s certainly within reach of a random plastic blob.
Looking closely at the printed-and-glued flange shows plenty of room for misunderstanding betwixt pen and holder, even after cleaning off all that PETG hair:
HP7475A – Sakura Micro Pen Adapter – vs HP pen
Given that the Sakura pens aren’t intended for this application, a slight tip misalignment due to body molding tolerances isn’t unreasonable; a perfect adapter might not solve the problem.
The HP maintenance manual lists a BASIC program to produce a test plot that verifies pen alignment, although the prospect of transliterating 2+ pages of quoted strings from a scanned document doesn’t fill me with desire.
You can buy new plotter pens for HP 7475A plotters at a bit over four bucks apiece and new-old-stock HP pens appear on eBay with similar prices, but what’s the fun in that?
You can refill the HP pens with liquid ink and continue plotting until the fiber tip wears out. That would limit me to the CMYK inkjet inks on the shelf, although I suppose investing in drafting inks might be amusing.
However, it should be feasible to build an adapter to hold an ordinary, albeit skinny, drawing / drafting pen, perhaps chopped down to be only a bit longer than the OEM plotter pens. That has the advantage of using cheap & readily available materials, doesn’t require much capital outlay, and, come to think of it, gives me a Digital Machinist column topic… [grin]
This is not, by any stretch of the imagination, a novel idea.
There’s a vague notion of converting the plotter into a vinyl / paper / stencil cutter, although I expect the snap-in pen holder can’t exert enough lateral force to hold a cutting knife in position, nor enough downward force to push the blade through the vinyl / paper / whatever. But ya never know until you try.
So, we begin…
A bit of digital caliper work provides a list of points defining the OEM pen body outline:
RADIUS = 0; // subscript for radius values
HEIGHT = 1; // ... height above Z=0
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
];
Rather than computing the radius by hand, it’s easier to just divide the easily measured diameter by two and be done with it.
The point array defines a polygon in the XY plane:
HP7475A – HP Plotter Pen Body – plane polygon
Then you feed that polygon into a rotate_extrude(), which spins up a reasonable simulacrum of a plotter pen:
HP7475A – HP Plotter Pen Body – solid model
I picked the coordinates to put the tip at (0,0,0) and converted the tapered fiber nib into a plain cylinder.
That shape is obviously impossible to print without vast amounts of support, but splitting it across the middle of the flange and rearranging the pieces works just fine:
HP7475A – HP Plotter Pen Body – build layout
A pair of alignment pin holes simplifies gluing the parts back together:
HP7475A – HP Plotter Pen Body – solid model – bottom
There’s a subtle problem lurking in that flange, which is 2.0 mm thick at the base and 1.0 mm thick at the rim. Splitting it in half requires each part to build correctly from an integral number of thread layers, so you must use a thread thickness (that’s in the Z direction) that divides evenly into the required height. I’ve been using 0.2 mm, which would produce a 1.2 mm rim.
Slicing at 0.25 mm produced a 2.1 mm flange with a 1.1 mm rim, suggesting that:
I could apply a Slic3r Modifier Mesh to print the flange with 0.10 mm layers, but that seems like entirely too much effort right now.
At the other end of the model, converting the tapered tip into a blunt cylinder didn’t save it from melting down:
HP 7475A Plotter Pen – solid PETG
It might be possible to reduce the printing speed enough to produce that tiny cylinder, but I needed just the upper body to verify that it fit correctly into the carousel:
HP 7475A Plotter Pen Body – in carousel
As you’d expect, the rubber boots that used to seal the pen tips have long since rotted out:
HP 7475A Carousel Rubber Boots
You can find sources for those boots, but at $252 (marked down to $144!) each, perhaps it’d be more feasible to gimmick up a two-part mold and cast silicone rubber duplicates; I could sell a set of six for $200 and get rich. Heck, I could even undercut their $40.32 two-year protection plan by a considerable margin.
Anyhow, the pen holder plucked it out of the carousel just like a real HP pen:
HP 7475A Plotter Pen Body – in holder
Note that the carousel and pen holder contact the flange and the cylindrical body, not either of the tapered sections down to the tip. That means I can carve away the entire bottom part of the body to make a drawing pen adapter…
The OpenSCAD source code includes a bunch of features & parts I’ll describe in the next few posts, but which certainly should not be regarded as final copy:
// HP7475A plotter pen adapters
// Ed Nisley KE4ZNU April 2015
Layout = "BuildBody"; // ShowKnife BuildKnife KnifeAdapter
// ShowPen BuildPen Plug
// ShowBody BuildBody
// Pen Knife
// 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];
PlugOutline = [
[0,0], // 0 center of lid
[9.5/2,0.0],[9.5/2,1.0], // 1 lid rim
[7.8/2,1.0], // 3 against end of pen
[7.3/2,6.0], // 4 taper inside pen
[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
];
//-- Drag knife holder
ExpRK = 0.30; // expand critical sections (by radius)
AdjLen = 2.0; // allowance for adjustment travel
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 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
];
ThreadLength = KnifeOutline[13][HEIGHT] - KnifeOutline[8][HEIGHT];
//-- 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];
//----------------------
// 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 Body() {
render(convexity=3)
rotate_extrude($fn=NumSides)
polygon(points=BodyOutline);
}
//-- HP plotter pen holder
// the trim block offsets use magic numbers from the HolderPlate outline
module Stabilizer() {
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])
Knife();
}
}
//-- 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(TrimZ = false) {
Trans = TrimZ ? - TrimHeight : 0;
render(convexity=5)
translate([0,0,Trans])
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);
}
}
//----------------------
// Build it
if (Layout == "Pen")
Pen();
if (Layout == "Knife")
Knife();
if (Layout == "Stabilizer")
Stabilizer();
if (Layout == "ShowBody")
Body();
if (Layout == "BuildBody")
difference() {
union() {
translate([BuildSpace,0,-SplitHeight])
Body();
rotate([180,0,0])
translate([-BuildSpace,0,-SplitHeight])
Body();
}
translate([0,0,-BodyOAL])
cube(2*BodyOAL,center=true);
for (i = [-1,1])
translate([i*BuildSpace,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 == "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 == "BuildPen") {
translate([0,BuildSpace/2,0])
Plug();
translate([0,-BuildSpace/2,-CapOffset])
Cap();
difference() {
union() {
translate([BuildSpace,0,-SplitHeight])
PenAdapter(false);
rotate([180,0,0])
translate([-BuildSpace,0,-SplitHeight])
PenAdapter(false);
}
translate([0,0,-BodyOAL])
cube(2*BodyOAL,center=true);
}
}
if (Layout == "BuildKnife") {
difference() {
union() {
translate([BuildSpace,0,-SplitHeight])
KnifeAdapter(false);
rotate([180,0,0])
translate([-BuildSpace,0,-SplitHeight])
KnifeAdapter(false);
}
translate([0,0,-BodyOAL])
cube(2*BodyOAL,center=true);
}
}
if (Layout == "BuildStabilizer") {
translate([0,0,(HolderPlateThick + RimHeight)])
rotate([0,180,0])
Stabilizer();
}
The user community asked for toned-down buttons, in place of my rather garish color scheme. A bit of twiddling with the Hue parameter produced these buttons:
Kenmore 158 UI – Pastel Buttons
Which look pretty good in context:
Kenmore 158 UI – Pastel buttons
The Bash script, which includes Unicode characters that may confuse your browser:
The trick depends on specifying the colors with HSB, rather than RGB, so that the buttons in each row have the same hue and differ in saturation and brightness. The Imagemagick incantations look like this:
Disabled: hsb\(${HUE}%,50%,40%\)
Unselected: hsb\(${HUE}%,100%,70%\)
Selected: hsb\(${HUE}%,100%,100%\)
For whatever reason, the hue must be a percentage if the other parameters are also percentages. At least, I couldn’t figure out how to make a plain integer without a percent sign suffix work as a degree value for hue.
Anyhow, in real life they look pretty good and make the selected buttons much more obvious:
The LCD screen looks just like that; I blew out the contrast on the surroundings to provide some context. The green square on the left is the Arduino Mega’s power LED, the purple dot on the right is the heartbeat spot.
The new “needle stop anywhere” symbol (left middle) is the White Draughts Man Unicode character: ⛀ = U+26C0. We call them checkers here in the US, but it’s supposed to look like a bobbin, as you must disengage the handwheel clutch and stop the main shaft when filling a bobbin; the needle positioning code depends on the shaft position sensor.
Weirdly, Unicode has no glyphs for sewing, not even a spool of thread, although “Fish Cake With Swirl” (🍥 = U+1F365) came close. Your browser must have access to a font with deep Unicode support in order to see that one…
First up: it’s not our projector, which means the usual Rules of Engagement do not apply.
A few small black plastic fragments fell out of the Epson S5 projector’s carry bag, the front foot wouldn’t remain extended, and, as one might expect, the two incidents were related. Mary needed it for the gardening class she was teaching the next evening, sooooo…
A pair of plastic snaps release the entire foot assembly from the front of the projector:
Epson S5 Projector Foot – assembled
It became obvious that we didn’t have all the fragments, but it was also obvious that, even if we had the pieces, a glued assembly wouldn’t last very long.
The threaded plastic stem surrounds a steel pin that’s visible when you remove the rubber foot pad. That pin holds the latch on the end of the stem outward, so that the stem can’t fall out. Drive out the pin with a (wait for it) pin punch inserted from the foot pad end, which reveals the broken plastic doodad:
Epson S5 Projector Foot – stem removed
Release the latches on the gray handle and the intricate half-nut that engages the threaded stem slides out:
Epson S5 Projector Foot – disassembled
A plastic spring in the boxy shell pushes the gray handle and half-nut against the stem, holding the stem in place. Pushing the gray handle upward (on the projector, downward in the picture, yes, your fingertip can feel those ribs just fine) pulls the half-nut away from the stem and lets the stem slide freely. With the stem extended, the projector leans on the stem, pushes it against the half-nut, and you can fine-tune the angle by turning the stem; the splines around the rubber foot encourage that. You can pull the stem outward without activating the latch, which probably broke the fragile plastic plate.
A doodle showing the estimated measurements, plus three 3D printed prototypes required to get a good fit:
Epson S5 Projector Foot – measurements and versions
The solid model looks about like you’d expect:
Epson S5 Projector foot latch – solid model
The first version (leftmost of the three sitting on the doodle, above) had angled ends on the tabs that I intended to match up with the stubs remaining on the OEM latch. The part fit better with shorter tabs and the angles vanished on third version; the statements remain in the OpenSCAD source, but the short tabs render them moot.
Apparently I got the cooling & fan & minimum layer time pretty close to right for PETG, as each of those three towers printed singly with no slumping:
Epson S5 Projector Foot – V1 on platform
The third version snapped into place, with a square of tapeless sticky on the back to help keep it there. The obligatory Kapton tape helps retain it, but I have no illusions about the permanence of this repair:
Epson S5 Projector Foot – repair installed
Because I know the problem will happen again, I called for backup:
Epson S5 Projector Foot – 5 copies
That’s with Hilbert Curve top / bottom fill, three top / bottom layers, 20% rectilinear infill, and two perimeters. Extruder at 250 °C, platform at 90 °C, hairspray for adhesion.
Note, however, the hair-fine strings connecting the towers. Retraction must be just about right, as shown by the overall quality of the objects, but PETG comes out really stringy. Choosing an infill pattern to minimize retraction seems like a big win; relatively sparse 3D Honeycomb works well on larger objects, but these were so small that straight line fill fit better. The flat plates on the bottom consist of five completely solid layers of PETG.
Reports from the field indicate complete success: whew!
One could, of course, just buy a replacement from the usual eBay supplier, if one were so inclined.
The remainder are closer to 1.4 mm = 3.3 threads, with the preview showing Slic3r allowed a narrow gap that doesn’t appear in real life:
Chain Mail – 3.3 wide – Slic3r preview – link bridge layer
What’s important about this is that the bridging worked perfectly: all the links emerged free of their neighbors and the patch flexed along both axes.
Chain mail – 6 and 4 thread
I tried this on one layer of Elmer’s White Glue, diluted 1:3 with water, and the links bonded firmly. I’d had some trouble with a few links popping off the usual hairspray after the first few layers, so I decided to try something different.
The fine hair strands have mostly Gone Away, perhaps due to using Concentric infill.
All in all, PETG looks pretty good, even if it’s just as hard to photograph as red PLA.
Update: You may prefer the source code as a GitHub gist.
The OpenSCAD source code:
// Chain Mail Armor Buttons
// Ed Nisley KE4ZNU - December 2014
Layout = "Build"; // Link Button LB Joiner Joiners Build PillarMod
//-------
//- Extrusion parameters must match reality!
// Print with 1 shell and 2+2 solid layers
ThreadThick = 0.25;
ThreadWidth = 0.40;
HoleWindage = 0.2;
Protrusion = 0.1; // make holes end cleanly
function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
//-------
// Dimensions
//- Set maximum sheet size
SheetSizeX = 55; // 170 for full sheet on M2
SheetSizeY = 55; // 230 ...
//- Diamond or rectangular sheet?
Diamond = false; // true = rotate 45 degrees, false = 0 degrees for square
BendAround = "X"; // X or Y = maximum flexibility *around* designated axis
Cap = true; // true = build bridge layers over links
CapThick = 4 * ThreadThick; // flat cap on link: >= 3 layers for solid bridging
Armor = true && Cap; // true = build armor button atop (required) cap
ArmorThick = IntegerMultiple(2.0,ThreadThick); // height above cap surface
ArmorSides = 4;
ArmorAngle = true ? 180/ArmorSides : 0; // true -> rotate half a side for best alignment
//- Link bar sizes
BarThick = 3 * ThreadThick;
BarWidth = 3.3 * ThreadWidth;
BarClearance = 4 * ThreadThick; // vertical clearance above & below bars
VertexHack = 0; // 0 = no, 1 = slightly reduce openings to avoid coincident vertices
//- Compute link sizes from those values
//- Absolute minimum base link: bar width + corner angle + build clearance around bars
// rounded up to multiple of thread width to ensure clean filling
BaseSide = IntegerMultiple((4*BarWidth + 2*BarWidth/sqrt(2) + 3*(2*ThreadWidth)),ThreadWidth);
BaseHeight = 2*BarThick + BarClearance; // both bars + clearance
echo(str("BaseSide: ",BaseSide," BaseHeight: ",BaseHeight));
//echo(str(" Base elements: ",4*BarWidth,", ",2*BarWidth/sqrt(2),", ",3*(2*ThreadWidth)));
//echo(str(" total: ",(4*BarWidth + 2*BarWidth/sqrt(2) + 3*(2*ThreadWidth))));
BaseOutDiagonal = BaseSide*sqrt(2) - BarWidth;
BaseInDiagonal = BaseSide*sqrt(2) - 2*(BarWidth/2 + BarWidth*sqrt(2));
echo(str("Outside diagonal: ",BaseOutDiagonal));
//- On-center distance measured along coordinate axis
// the links are interlaced, so this is half of what you think it should be...
LinkOC = BaseSide/2 + ThreadWidth;
LinkSpacing = Diamond ? (sqrt(2)*LinkOC) : LinkOC;
echo(str("Base spacing: ",LinkSpacing));
//- Compute how many links fit in sheet
MinLinksX = ceil((SheetSizeX - (Diamond ? BaseOutDiagonal : BaseSide)) / LinkSpacing);
MinLinksY = ceil((SheetSizeY - (Diamond ? BaseOutDiagonal : BaseSide)) / LinkSpacing);
echo(str("MinLinks X: ",MinLinksX," Y: ",MinLinksY));
NumLinksX = ((0 == (MinLinksX % 2)) && !Diamond) ? MinLinksX + 1 : MinLinksX;
NumLinksY = ((0 == (MinLinksY % 2) && !Diamond)) ? MinLinksY + 1 : MinLinksY;
echo(str("Links X: ",NumLinksX," Y: ",NumLinksY));
//- Armor button base
ButtonHeight = BaseHeight + BarClearance + CapThick;
echo(str("ButtonHeight: ",ButtonHeight));
//- Armor ornament size & shape
// Fine-tune OD & ID to suit the number of sides...
TotalHeight = ButtonHeight + ArmorThick;
echo(str("Overall Armor Height: ",TotalHeight));
ArmorOD = 1.0 * BaseSide; // tune for best base fit
ArmorID = 10 * ThreadWidth; // make the tip blunt & strong
//-------
module ShowPegGrid(Space = 10.0,Size = 1.0) {
RangeX = floor(95 / Space);
RangeY = floor(125 / Space);
for (x=[-RangeX:RangeX])
for (y=[-RangeY:RangeY])
translate([x*Space,y*Space,Size/2])
%cube(Size,center=true);
}
//-------
// Create link with armor button as needed
module Link(Topping = false) {
LinkHeight = (Topping && Cap) ? ButtonHeight : BaseHeight;
render(convexity=3)
rotate((BendAround == "X") ? 90 : 0)
rotate(Diamond ? 45 : 0)
union() {
difference() {
translate([0,0,LinkHeight/2]) // outside shape
intersection() {
cube([BaseSide,BaseSide,LinkHeight],center=true);
rotate(45)
cube([BaseOutDiagonal,BaseOutDiagonal,(LinkHeight + 2*Protrusion)],center=true);
}
translate([0,0,(BaseHeight + BarClearance + 0*ThreadThick - Protrusion)/2])
intersection() { // inside shape
cube([(BaseSide - 2*BarWidth),
(BaseSide - 2*BarWidth),
(BaseHeight + BarClearance + 0*ThreadThick + VertexHack*Protrusion/2)],
center=true);
rotate(45)
cube([BaseInDiagonal,
BaseInDiagonal,
(BaseHeight + BarClearance + 0*ThreadThick + VertexHack*Protrusion/2)],
center=true);
}
translate([0,0,((BarThick + 2*BarClearance)/2 + BarThick)]) // openings for bars
cube([(BaseSide - 2*BarWidth - 2*BarWidth/sqrt(2) - VertexHack*Protrusion/2),
(2*BaseSide),
BarThick + 2*BarClearance - Protrusion],
center=true);
translate([0,0,(BaseHeight/2 - BarThick)])
cube([(2*BaseSide),
(BaseSide - 2*BarWidth - 2*BarWidth/sqrt(2) - VertexHack*Protrusion/2),
BaseHeight],
center=true);
}
if (Topping && Armor)
translate([0,0,(ButtonHeight - Protrusion)]) // sink slightly into the cap
rotate(ArmorAngle)
cylinder(d1=ArmorOD,d2=ArmorID,h=(ArmorThick + Protrusion), $fn=ArmorSides);
}
}
//-------
// Create split buttons to join sheets
module Joiner() {
translate([-LinkSpacing,0,0])
difference() {
Link(false);
translate([0,0,BarThick + BarClearance + TotalHeight/2 - Protrusion])
cube([2*LinkSpacing,2*LinkSpacing,TotalHeight],center=true);
}
translate([LinkSpacing,0,0])
intersection() {
translate([0,0,-(BarThick + BarClearance)])
Link(true);
translate([0,0,TotalHeight/2])
cube([2*LinkSpacing,2*LinkSpacing,TotalHeight],center=true);
}
}
//-------
// Build it!
//ShowPegGrid();
if (Layout == "Link") {
Link(false);
}
if (Layout == "Button") {
Link(true);
}
if (Layout == "LB") {
color("Brown") Link(true);
translate([LinkSpacing,LinkSpacing,0])
color("Orange") Link(false);
}
if (Layout == "Build")
for (ix = [0:(NumLinksX - 1)],
iy = [0:(NumLinksY - 1)]) {
x = (ix - (NumLinksX - 1)/2)*LinkSpacing;
y = (iy - (NumLinksY - 1)/2)*LinkSpacing;
translate([x,y,0])
color([(ix/(NumLinksX - 1)),(iy/(NumLinksY - 1)),1.0])
if (Diamond)
Link((ix + iy) % 2); // armor at odd,odd & even,even points
else
if ((iy % 2) && (ix % 2)) // armor at odd,odd points
Link(true);
else if (!(iy % 2) && !(ix % 2)) // connectors at even,even points
Link(false);
}
if (Layout == "Joiner")
Joiner();
if (Layout == "Joiners") {
NumJoiners = max(MinLinksX,MinLinksY)/2;
for (iy = [0:(NumJoiners - 1)]) {
y = (iy - (NumJoiners - 1)/2)*2*LinkSpacing + LinkSpacing/2;
translate([0,y,0])
color([0.5,(iy/(NumJoiners - 1)),1.0])
Joiner();
}
}
if (Layout == "PillarMod") // Slic3r modification volume to eliminate pillar infill
translate([0,0,(BaseHeight + BarClearance)/2])
cube([1.5*SheetSizeX,1.5*SheetSizeY,BaseHeight + BarClearance],center=true);