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.

Category: Machine Shop

Mechanical widgetry

  • Refrigerator Drawer Strut Tab: Now With Inserts

    A spate of cleaning put the little tab that fixed the never-sufficiently-to-be-damned strut supporting the lower refrigerator drawers into my hands:

    Refrigerator Drawer Strut - new tab in place
    Refrigerator Drawer Strut – new tab in place

    I discovered that 4-40 knurled inserts perfectly match the available space, so I drilled the 3D printed holes out to 11/16 inch (the OD of the smaller knurls) and rammed the inserts into place:

    Refrigerator Drawer Strut Tab - knurled inserts
    Refrigerator Drawer Strut Tab – knurled inserts

    No epoxy, no heat, nothing but a friction fit.

    Looks much better, ought to work just as well, and will definitely outlive the refrigerator; if I never take that thing apart again, it’ll be fine with me.

  • Filament Drive Gear Calculations

    Some equations relevant to indentations produced by a filament drive gear:

    Filament Drive Gear Indentations
    Filament Drive Gear Indentations

    For reference, the smaller indentations are 0.25 mm deep and 1.3 mm across the bottom.

    Variables:

    • d = filament (a.k.a. circle) diameter
    • r = filament radius
    • m = chord depth (inward from circle)
    • c = chord length
    • Θ = angle across chord from circle center, degrees
    • A = chord area (a.k.a. indentation face area)

    The length of the chord at the bottom of the indentation, perpendicular to the filament axis:

    c = 2 sqrt(2mr - m2)

    The chord angle:

    Θ = 2 arcsin(c/2r)

    The chord area, which would be the indentation face if it were perpendicular, which it isn’t:

    A = (r2 / 2) x ((πΘ / 180) - sin(Θ))

    If you measured Θ in radians, the π/180 factor would Go Away.

    Some doodles showing that reducing the indentation from 0.25 to 0.15 reduces the chord area by a factor of two:

    Filament Drive Gear - indentation doodles
    Filament Drive Gear – indentation doodles

    The implication being that you must maintain fairly constant force on the drive bearing against the filament to prevent stripping the indentations.

  • M2 Platform Alignment Check

    Five single-thread thinwall boxes scattered across the platform had an average height of  2.99 mm, with a range of +0.04 mm, -0.06 mm:

    Thinwall open box - 1 thread walls
    Thinwall open box – 1 thread walls

    The wall widths work out to 0.39 mm, with a range of +0.2 mm, -0.01 mm.

    Close enough, given that I can’t recall the last time I tweaked the platform height. I update the filament diameter setting in Slic3r every now & again as the printer gradually works through the spool, but, with one exception, this cyan PETG has been quite consistent and my tweaks didn’t really amount to much.

    Frankly, given that any of the measurements may be off by ±0.02, the best I can hope for is an overall warm fuzzy feeling. When the printed results stop looking good, these results will (probably) provide some indication of whatever just changed.

    The raw measurement data, such as it is:

    Thinwall box measurements - 2016-04-01
    Thinwall box measurements – 2016-04-01
  • Thinwall and Solid Boxes for 3D Printer Calibration

    A revision to my Fundamental Calibration Object adds some variations …

    The classic thinwall open box:

    Calibration Box - open - 1 thread - solid model
    Calibration Box – open – 1 thread – solid model

    A solid box:

    Calibration Box - solid - solid model
    Calibration Box – solid – solid model

    A solid box with text embossed on the lower surface:

    Calibration Box - solid text - solid model
    Calibration Box – solid text – solid model

    You must consider how the slicer settings interact with the solid model parameters, particularly now that slicers can produce adaptive infill for small gaps between perimeter threads. Previewing the slicer’s output will show you what assumptions it makes and prevent surprising results out there on the platform.

    A single-thread wall comes out properly:

    Thinwall open box - 0.40 wall - Slic3r
    Thinwall open box – 0.40 wall – Slic3r

    The results look just like the preview, with firmly bonded layers and no fluff:

    Thinwall open box - 1 thread walls
    Thinwall open box – 1 thread walls

    This wall should be two threads wide, but Slic3r inserts very very thin infill thread:

    Thinwall open box - 0.80 wall - Slic3r
    Thinwall open box – 0.80 wall – Slic3r

    I think that’s a result of forcing the two perimeter threads to sit with their centers exactly one thread width apart, making the (nominal, ideal) inner walls tangent to each other.  Setting the wall to 1.9 mm eliminates the hair-fine infill thread, at the cost of producing an object 0.1 mm smaller than it looks.

    Unfortunately, that fine infill doesn’t produce enough plastic flow for a continuous thread. The PET I’m using accumulates on the nozzle until enough of a glob forms to stick on the previous layer, but hair-fine strands connect those globs to each other and the nozzle, producing awful results:

    Thinwall open box - 2 thread walls
    Thinwall open box – 2 thread walls

    A triple-thread wall allows Slic3r to produce a fatter infill thread that works the way you’d expect:

    Thinwall open box - 1.20 wall - Slic3r
    Thinwall open box – 1.20 wall – Slic3r

    The threads bond firmly in all directions:

    Thinwall open box - 3 thread walls
    Thinwall open box – 3 thread walls

    It’s not obvious from that picture, but the bond between successive infill threads produces a glass-clear vertical plastic slab that relays images from the bottom to the top. The perimeter threads are also firmly bonded, albeit with not quite the same optical quality.

    To use these boxes:

    • Set the OpenSCAD extrusion parameters to match whatever the slicer will use
    • Set the wall height and thickness to whatever you like
    • Compile-and-render, export the result as a solid model in STL / AMF / whatever
    • Feed the solid model into your favorite slicer and save the G-Code
    • Feed the G-Code into your printer, watch it magically create a little box
    • Measure the printed results and compare with the ideal settings
    • Change the slicing configuration and iterate until satisfied

    Verify these measurements before adjusting anything else:

    • Filament diameter: actual vs. nominal will be different
    • Extruder steps per millimeter: mark 100 mm on filament, extrude 100 mm, compare

    Then you can verify / adjust some finicky settings:

    • Extrusion multiplier: does the actual single wall width match slicer’s nominal value?
    • Infill density: 100% infill should perfectly fill the solid box
    • Initial Z offset: does actual height match the model setting?
    • Platform alignment: print five boxes at platform center + corners, verify heights
    • First layer adhesion: if these don’t stick, the platform has weak adhesion
    • Minimum time per layer: if the walls slump, you’re printing too fast
    • Extrusion temperature: good bonding and no delamination along any axis

    The OpenSCAD source code as a GitHub gist:

    // Simple calibration boxes
    // Thin wall open box – verify Extrusion Multiplier
    // Solid box – verify infill settings
    // Ed Nisley – KE4ZNU
    // https://softsolder.com/
    Layout = "Open"; // Open Solid
    Texting = "Text!"; // text message on solid box or empty string to suppress
    //——-
    //- Extrusion parameters must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    Protrusion = 0.1; // make holes end cleanly
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    //——-
    // Dimensions
    WallThick = 1.0 * ThreadWidth;
    echo(str("Wall thickness: ",WallThick));
    BoxSize = 20.0;
    echo(str("Overall size: ",BoxSize));
    NominalHeight = 3.0;
    echo(str("Nominal height: ",NominalHeight));
    Height = IntegerMultiple(NominalHeight,ThreadThick);
    echo(str("Actual height: ",Height));
    Rotation = 0; // 45 to exercise X and Y axis motors at same time
    CornerRadius = 2.0;
    CornerSides = 8*4;
    //——–
    module Solid() {
    difference() {
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(BoxSize – 2*CornerRadius)/2,j*(BoxSize – 2*CornerRadius)/2,0])
    cylinder(r=CornerRadius,h=Height,$fn=CornerSides);
    if (len(Texting))
    translate([0,0,-Protrusion/2])
    linear_extrude(height=3*ThreadThick + Protrusion)
    mirror([1,0,0])
    text(text=Texting,size=6,spacing=1.05,font="ITC Zapf Chancery:style=Italic",halign="center",valign="center");
    }
    }
    module Thinwall() {
    difference() {
    Solid();
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(BoxSize – 2*CornerRadius)/2,j*(BoxSize – 2*CornerRadius)/2,-Protrusion])
    cylinder(r=(CornerRadius – WallThick),h=(Height + 2*Protrusion),$fn=CornerSides);
    }
    }
    //——-
    rotate(Rotation)
    if (Layout == "Open")
    Thinwall();
    else
    Solid();
  • Kenmore 158.17032: Mystery Spring

    This steel strip emerged from inside the arm of the Kenmore 158.17032 sewing machine that we’ve been reconditioning for one of Mary’s friends:

    Kenmore 158.17032 - mystery spring
    Kenmore 158.17032 – mystery spring

    The ends show the granular fracture of hard steel:

    Kenmore 158.17032 - mystery spring - end view
    Kenmore 158.17032 – mystery spring – end view

    It’s 13.3 mm long, 1.0 mm thick, tapers slightly from 2.8 mm on the end that once said “Japan” to 2.76 mm on the other, and that’s all we know about it.

    The sewing machine seems to work well enough without it (after some clean-and-lube action) and we haven’t found where the piece came from, but circumstantial evidence suggests it’s part of a spring somewhere inside the arm. It’s in a little bag with all the other random sewing machine parts I’ve collected along the way; perhaps some day we’ll know more and I can fabricate a replacement.

  • Square Chain Mail Armor: Back From The Abyss

    After a Slic3r commit fixed the bridging regression, I ran off chain mail patches to celebrate:

    Square Chain Mail Armor - 3.3 3.5 4.0 thread bars
    Square Chain Mail Armor – 3.3 3.5 4.0 thread bars

    Two more Scli3r improvements calculate thin-wall and gap infill based on the available space, then vary the extrusion width to make the answers come out right for a given nozzle diameter. As a result, infill between close-set perimeter walls works much better than before; some of my long-held assumptions became invalid.

    The only differences between the sheets: tweaking the BarWidth and SheetSize parameters. The links recalculate themselves around those values.

    The OpenSCAD source code as a GitHub gist:

    // 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 = 125; // 170 for full sheet on M2
    SheetSizeY = 125; // 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 = 3 * ThreadThick; // vertical clearance above & below bars
    VertexHack = false; // true to 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 : 0))],
    center=true);
    rotate(45)
    cube([BaseInDiagonal,
    BaseInDiagonal,
    (BaseHeight + BarClearance + 0*ThreadThick + (VertexHack ? Protrusion/2 : 0))],
    center=true);
    }
    translate([0,0,((BarThick + 2*BarClearance)/2 + BarThick)]) // openings for bars
    cube([(BaseSide – 2*BarWidth – 2*BarWidth/sqrt(2) – (VertexHack ? Protrusion/2 : 0)),
    (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 : 0)),
    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);
  • Recommended Screwdriver Set: Brownells Magna-Tip Super Set

    More on the Kenmore 158.17032 that started all this appears elsewhere, but I found myself deploying several bits from my Brownells Magna-Tip screwdriver set:

    Brownells Magna-Tip Super-Set on bench
    Brownells Magna-Tip Super-Set on bench

    The matrix of bits covers nine slot lengths (= screw head diameters) with four / five / six slot widths. This is the set with 44 bits; the 58 bit set fills the empty holes with 14 hex / square / Phillips bits that I already have in multiples.

    I reserve these lovely hollow-ground bits for specialty screws that must not be goobered; most of the time ordinary drivers work just fine and there’s no reason to chew these up.

    Even these tips won’t fit every screw in existence, but you’ll go a long way before this set isn’t the right hammer for the job at hand.

    Highly recommended, even at today’s prices …