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: Software

General-purpose computers doing something specific

  • Punched Cards: Summary

    Punched Cards: Summary

    At last, I can make plausible-looking punched cards:

    Test Card 3 - punched
    Test Card 3 – punched

    Then chop most of them up to make a layered eagle:

    Apollo Eagle - V3 - overview
    Apollo Eagle – V3 – overview

    Back in the beginning, the grand overview explained the card production process, but now I can pull all the blog posts into a more coherent story.

    Start by making trays to hold the 1/3 Letter sized printed cards and the final cut cards. A coat of paint improves the result:

    Card Storage Tray - front
    Card Storage Tray – front

    Then make a fixture to position the 1/3 Letter printed cards in the laser and a simple cover for the honeycomb to direct the air flow:

    Punched cards - laser fixture overview
    Punched cards – laser fixture overview

    The current versions of the Python program to convert a line of text into the SVG images required to print and punch the cards, plus the Bash scripts handling all the command line parameters, are now in a single GitHub Gist . I used the source code from the Apollo 11 CSM AGC for historic reasons.

    The Bash scripts invoke the Python program twice to produce both the printed layout:

    Punched Cards - test card - printed
    Punched Cards – test card – printed

    And “punched” holes surrounded by the perimeter cut for the laser:

    Test Card 3 - LightBurn layout
    Test Card 3 – LightBurn layout

    The Python program handles translation from the ASCII (really Unicode) character set into the EBCDIC punched hole layout. Because LightBurn and Inkscape handle SVG scaling differently, the script sorts that out.

    Because my printer produces slightly off-size printed images, the script uses Inkscape to convert the SVG into a PNG, then downscales the image by a few percent (a different percent on each axis). It composites the card logo onto the PNG and slams the result onto a Letter page in the proper place to hit the 1/3 Letter sheets.

    Aligning the targets printed on the cards with the corresponding target positions in the laser SVG requires careful fixture skootching:

    Red dot vs printed target vs laser spot alignment
    Red dot vs printed target vs laser spot alignment

    A batch file feeds the laser SVGs into LightBurn, so the process boils down to a few mouse clicks per card.

    With a tray full of finished cards in hand, I converted the eagle from the Apollo 11 mission patch into a set of outlines:

    Apollo 11 Patch - eagle layers
    Apollo 11 Patch – eagle layers

    Each of those outlines defines the shape of a layer cut from those printed cards:

    Apollo Eagle - V3 - head
    Apollo Eagle – V3 – head

    Not gonna lie: it took serious effort to cut up those cards.

    Each layer has a specific set of cards chosen to put the holes in the proper place while hiding the card joints:

    Apollo Eagle - V4 Layer 1 cards
    Apollo Eagle – V4 Layer 1 cards

    Mirroring the layout helped me arrange the cards correctly while taping the back side of the joints with book repair tape:

    Apollo Eagle - V4 Layer 1 cards - mirrored
    Apollo Eagle – V4 Layer 1 cards – mirrored

    Slap a sheet of cards on the laser platform, align it to the layer’s outline, Fire The Laser, and stack up the results:

    Apollo Eagle - V3 - tail
    Apollo Eagle – V3 – tail

    I used Elmer’s All Purpose Glue Stick to hold the layers together, figuring if it’s good enough for kindergartners it’s good enough for me.

    And that’s all there is to it …

  • Gridfininty Tape Dispenser

    Gridfininty Tape Dispenser

    A Gridfinity Tape Dispenser holds a roll of book repair tape:

    Gridfinity Tape Dispenser - overview
    Gridfinity Tape Dispenser – overview

    The perspective makes the dispenser look chonkier than it really is.

    A wrap of black silicone tape around the spool embiggens it for a snug fit inside the tape core. A casual inspection of other tapes suggest enlarging the spool by a few percent would help, but it’s Good Enough™ as-is.

    The two end thumbscrews fasten the 4×1 Gridfinity baseplate to the dispenser; both from Gridfinity Refined:

    Gridfinity Tape Dispenser - baseplate
    Gridfinity Tape Dispenser – baseplate

    If I had my wits about me, I’d have used a nicely contrasting color for the baseplate, but it is what it is.

    Although they’re called “thumbscrews”, the slot is sized for a US quarter (or cart coin).

    An OpenSCAD one-liner produces an SVG model of the baseplate:

    projection(cut=true) import("Grid 4x1.stl");
    

    Import SVG into LightBurn, delete the magnet pockets, and Fire The Laser on some EVA foam:

    Gridfinity Tape Dispenser - foam base
    Gridfinity Tape Dispenser – foam base

    A layer of 3M 300LSE tape holds the foam in place, because neither side sticks well to the goo on a craft adhesive sheet due to their low surface energy. I stuck an oversize rectangle to the foam with the thin adhesive side before cutting, which required a second pass at higher speed.

    The thumbscrews also close off the holes in the dispenser bottom through which I poured 275 g = 10 oz of sand for better traction. Steel shot is reputed to be Even Better, although most of the BBs are in the long-arm weight.

    The dispenser model includes a printed serrated blade which works as poorly as the author suggested. A length snapped from an ancient Strombecker 4-I (“four eye”) blade in the Box o’ Big X-Acto Blades fits perfectly, works wonderfully well, and is sufficiently inconspicuous to warrant the warning label. An X-Acto #26 Whittling Blade would probably snap down equally well.

  • LED Garage Light: Desk Lamp Upcycling

    LED Garage Light: Desk Lamp Upcycling

    One of the heatsink panels from the defunct LED garage light now casts a uniform warm-white glow on my desk:

    LED Garage Light - desk light
    LED Garage Light – desk light

    A PCB intended as a lithium battery charger serves as a constant-current supply:

    LED Garage Light - constant current driver
    LED Garage Light – constant current driver

    The three trimpots, from left to right:

    • Constant-voltage limit adjustment
    • Full-charge current setpoint (irrelevant here)
    • Constant-current limit adjustment

    The as-received trimpot settings will be wildly inappropriate for a nominal 10 W COB LED array, so:

    • Connect the output to about 10 Ω of power resistors
    • … with an ammeter in series
    • Connect the input to a 12 VDC / 1-ish A wall wart
    • Adjust the output voltage to 10 V
    • Adjust the output current to 900 mA

    As long as the voltage limit is over about 10 V, it will (likely) never matter, as the LED forward drop doesn’t vary much with temperature. Setting it to something sensible keeps it out of the way.

    The middle trimpot apparently sets a voltage for a comparator to light an LED when the battery current drops below that level as it reaches full charge.

    Although the regulator touts its high efficiency, it does run hot and a heatsink seemed in order:

    LED Garage Light - heatsink
    LED Garage Light – heatsink

    Stipulated: the fins run the wrong way and it’s sitting in the updraft from the main heatsink. It’s Good Enough™.

    The switch on the top comes from the collection of flashlight tailcap switches and controls the 12 V input power. It’s buried up to its button in a generous dollop of JB Kwik epoxy, which seemed the least awful way to get that done.

    The solid model looks about like you’d expect:

    LED Lamp Driver case - switch housing - show solid model
    LED Lamp Driver case – switch housing – show solid model

    The OpenSCAD code exports the (transparent) lid as an SVG so I can import it into LightBurn and laser-cut some thin acrylic. Two tape snippets hold the lid in place pending more power-on hours, after which I’ll apply a few dots of cyanoacrylate adhesive and call it done.

    The case builds in two pieces that glue together to avoid absurd support structures:

    LED Lamp Driver case - switch housing - build solid model
    LED Lamp Driver case – switch housing – build solid model

    A 3D printed adapter goes between the desk lamp arm and the lamp heatsink bolt:

    LED Lamp Driver case - arm adapter - solid model
    LED Lamp Driver case – arm adapter – solid model

    The OpenSCAD source code files for the case and adapter arm as a GitHub Gist:

    // LED Lamp arm adapter
    // Ed Nisley – KE4ZNU
    // 2026-03-18
    include <BOSL2/std.scad>
    Layout = "Adapter"; // [Show,Build,ArmClamp,SinkClamp,Adapter]
    /* [Hidden] */
    HoleWindage = 0.2;
    Protrusion = 0.01;
    Gap = 5.0;
    $fn=5*3*4;
    HoleOC = 45.0;
    ArmRad = 7.5;
    ArmWidth = 11.3;
    SinkOD = 11.5;
    SinkThick = 3.2;
    SinkOC = 20.0;
    ClampThick = 5.0; // outside sink, watch thinning due to hull()
    // Define things
    // Screw & bushings in lamp arm bracket
    // … over-long bushings to prevent coincident surfaces
    module ArmClamp() {
    BushingThick = 1.5;
    BushingOD = 9.0;
    union() {
    ycyl(ArmWidth,d=4.0 + HoleWindage); // central M4 screw
    for (j=[-1,1]) {
    back(j*(ArmWidth – BushingThick + Protrusion)/2)
    ycyl(BushingThick + Protrusion,d=BushingOD);
    back(j*(ArmWidth + 10)/2)
    cuboid([2*ArmRad,10,2*ArmRad]);
    }
    }
    }
    module SinkClamp() {
    union() {
    ycyl(2*SinkOC,d=6.0 + HoleWindage); // central M6 screw
    for (j=[-1,1])
    back(j*SinkOC/2) {
    ycyl(SinkThick + Protrusion,d=SinkOD);
    cuboid([SinkOD,SinkThick + Protrusion,2*SinkOD]);
    }
    }
    }
    module Adapter() {
    difference() {
    hull() {
    right(HoleOC)
    ycyl(ArmWidth,r=ArmRad);
    ycyl(SinkOC + SinkThick + 2*ClampThick,d=SinkOD);
    }
    right(HoleOC)
    ArmClamp();
    SinkClamp();
    }
    }
    // Build it
    if (Layout == "ArmClamp")
    ArmClamp();
    if (Layout == "SinkClamp")
    SinkClamp();
    if (Layout == "Adapter")
    Adapter();
    if (Layout == "Build")
    up(SinkOD/2)
    yrot(-atan((ArmRad – SinkOD/2)/HoleOC))
    Adapter();
    // LED Constant-current driver case
    // Ed Nisley – KE4ZNU
    // 2026-03-15
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build,Case,Lid,LidSVG,Switch]
    /* [Hidden] */
    ThreadThick = 0.2;
    HoleWindage = 0.2;
    Protrusion = 0.01;
    Gap = 5.0;
    WallThick = 1.8;
    TapeThick = 1.5;
    DriverOA = [48.5,13.5 + TapeThick,23.5]; // PCB forward Y, pots along top to rear
    SinkOA = [31.5,12.0,15.5]; // fins forward
    SinkOffset = [(DriverOA.x – SinkOA.x)/2,0,2.0]; // from lower left front corner of PCB
    AdjPots = [14,24,34]; // screwdriver adjust offsets
    AdjOD = 3.0; // … access hole dia
    CaseOA = DriverOA + [2*WallThick,2*WallThick,2*WallThick];
    echo(CaseOA=CaseOA);
    LidOA = [CaseOA.x – WallThick,CaseOA.z – WallThick,1.0];
    Cables = [8.0,3.0 + WallThick/2,LidOA.z];
    SwitchWireOC = DriverOA.x – 6.0;
    SwitchCapBase = [DriverOA.x + WallThick,DriverOA.y + WallThick];
    SwitchCapTop = [DriverOA.x,12.0];
    SwitchCavity = [25.0,10.5,5.5];
    // Define things
    module Lid() {
    difference() {
    cuboid(LidOA,anchor=BOTTOM+FWD+LEFT);
    for (i = AdjPots)
    translate([i,LidOA.y – AdjOD/2 – WallThick/2,-Protrusion])
    cyl(LidOA.z + 2*Protrusion,d=AdjOD,anchor=BOTTOM,$fn=8,spin=180/8);
    translate([LidOA.x/2,-Protrusion,-Protrusion])
    cuboid(Cables + [0,Protrusion,2*Protrusion],rounding=1.0,edges=[BACK+LEFT,BACK+RIGHT],anchor=BOTTOM+FWD);
    }
    }
    module SwitchBox() {
    difference() {
    prismoid(SwitchCapBase,SwitchCapTop,SwitchCavity.z,anchor=BOTTOM);
    down(Protrusion)
    cuboid(SwitchCavity + [0,0,2*Protrusion],anchor=BOTTOM);
    hull()
    for (i=[-1,1])
    right(i*SwitchWireOC/2)
    zcyl(CaseOA.z,d=3.0,$fn=8,spin=180/8);
    }
    }
    module Case() {
    difference() {
    cuboid(CaseOA,chamfer=WallThick/2,anchor=BOTTOM+FWD+LEFT);
    translate([WallThick,WallThick + Protrusion,WallThick])
    cuboid(DriverOA + [0,WallThick + Protrusion,0],anchor=BOTTOM+FWD+LEFT);
    translate(SinkOffset + [WallThick,WallThick + 2*Protrusion,WallThick])
    cuboid(SinkOA,anchor=BOTTOM+BACK+LEFT);
    for (i=[-1,1])
    translate([i*SwitchWireOC/2 + CaseOA.x/2,CaseOA.y/2,CaseOA.z/2])
    zcyl(CaseOA.z,d=2.0,anchor=BOTTOM,$fn=8,spin=180/8);
    translate([WallThick/2,(CaseOA.y + LidOA.z),WallThick/2])
    xrot(90)
    scale([1,1,2])
    Lid();
    }
    }
    // Build it
    if (Layout == "Switch")
    SwitchBox();
    if (Layout == "Case")
    Case();
    if (Layout == "Lid")
    Lid();
    if (Layout == "LidSVG")
    projection(cut=true)
    Lid();
    if (Layout == "Show") {
    Case();
    translate(SinkOffset + [WallThick,WallThick + 2*Protrusion,WallThick])
    color("Gray",0.7)
    cuboid(SinkOA,anchor=BOTTOM+BACK+LEFT);
    translate([CaseOA.x/2,CaseOA.y/2,CaseOA.z])
    SwitchBox();
    translate([WallThick/2,CaseOA.y,WallThick/2])
    xrot(90)
    color("Gray",0.7)
    Lid();
    }
    if (Layout == "Build") {
    fwd(Gap)
    xrot(90)
    Case();
    translate([CaseOA.x/2,(Gap + CaseOA.y/2),0])
    SwitchBox();
    }
  • HQ Sixteen: Bobbin Winder Adjustment and Unwind Adapter

    HQ Sixteen: Bobbin Winder Adjustment and Unwind Adapter

    The Industrial Age bobbin winder for Mary’s HQ Sixteen long-arm machine bunched the thread on one end of the bobbin, rather than distributing it in even layers as it should. Tinkering with the thread tension setting being unavailing, I settled in for some debugging.

    After filling two bobbins from a spool of the thread Mary uses for practice quilts, I decided I should reuse the thread. Mounting the filled bobbin on a 6 mm horizontal shaft attached to the vertical pin normally locating the spool let the thread pay out in the proper orientation, with a duct-tape lashup holding the shaft in place:

    HQ Sixteen bobbin unwind adapter - expedient version
    HQ Sixteen bobbin unwind adapter – expedient version

    I added the stack of washers to keep the bobbin away from the duct tape after having the tape’s adhesive migrate onto the spinning bobbin.

    The thread from the spool or, in my case, a filled bobbin, passes between a pair of tension disks on its way to the bobbin spun by the motor:

    HQ Sixteen bobbin winder - thread path
    HQ Sixteen bobbin winder – thread path

    A conical spring presses the tension disks together, with the thread clamped between them:

    HQ Sixteen bobbin winder - tension disk overview
    HQ Sixteen bobbin winder – tension disk overview

    The instructions suggest using “the lightest tension possible”, but backing the nut off to hang by its fingernails had no effect. The spring has a bent end passing through the slotted shaft, so rotation of the disks won’t unscrew the nut.

    The washer under the mounting screw left slight scars in the black oxide finish on the fixture, presumably from previous attempts to adjust the thing:

    HQ Sixteen bobbin winder - tension disk base
    HQ Sixteen bobbin winder – tension disk base

    The threaded shaft is not exactly parallel to the base, because the upright arm is slightly over-bent, but I think that has no effect on the outcome, because the thread path doesn’t depend on the disk angle.

    Because the thread accumulated on the outer side of the bobbin (to the right in that picture), I loosened the mounting screw and shoved the fixture all the way to the left. That should, if anything, bias the thread accumulation to the other (inner) side of the bobbin.

    As it turned out, relocating the tension disks caused the thread to distribute evenly across the bobbin, with only occasional hesitations and no significant accumulations; Mary pronounced the result entirely satisfactory.

    The motor dataplate says it runs at 7000 RPM, so the 3/4 inch O-ring drives the 4 inch wheel at about 1300 RPM. This was sufficiently terrifying I immediately set up a triac speed control (intended for a router) to throttle it down, but with the bobbins now filling properly we run the motor at full speed and it fills a bobbin in 23 seconds flat.

    After we filled half a dozen bobbins with blue thread for the quilt project, I conjured an adapter from the vasty digital deep for a snippet of 6 mm rod with a D-shaped end:

    Bobbin Unwind Adapter - solid model - show
    Bobbin Unwind Adapter – solid model – show

    The adapter builds on one leg, with a brim for stability:

    HQ Sixteen bobbin unwind adapter - on platform
    HQ Sixteen bobbin unwind adapter – on platform

    And looks like it belongs there:

    HQ Sixteen bobbin unwind adapter - installed
    HQ Sixteen bobbin unwind adapter – installed

    It’s now in the box of HQ Sixteen bobbins, where we both hope it will remain undisturbed forevermore.

    Although the vertical pin locating the spools (and holding the adapter) is nominally 6 mm, burrs in the chrome plating prevented the bobbin’s 6 mm bore from sliding over it. In retrospect, that prevented me from just dropping the bobbin on the pin and unwinding the thread over the side of the bobbin, which likely avoided some serious-to-lethal thread tangles.

    After all that debugging, I had several bobbins full of well-worn thread, so:

    • Chuck a chopstick in the mini-lathe
    • Tape thread to chopstick
    • Put bobbin on another 6 mm shaft
    • Run lathe at a reasonable speed
    • Produce what looks very much like a cocoon
    HQ Sixteen bobbin unwind adapter - thread cocoon
    HQ Sixteen bobbin unwind adapter – thread cocoon

    All’s well that ends well.

    The OpenSCAD source code as a GitHub Gist:

    // HQ Sixteen Bobbin Winder – Unwind adapter
    // Ed Nisley – KE4ZNU
    // 2026-03-27
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build]
    /* [Hidden] */
    HoleWindage = 0.2;
    Protrusion = 0.01;
    Gap = 5.0;
    $fn=5*3*4;
    WallThick = 2.0;
    SpoolRodOD = 6.03; // vertical spool rod
    Kerf = 0.5;
    BobbinRodOD = 6.0 + HoleWindage; // horizontal bobbin rod
    BobbinRodDee = 4.95; // … remaining rod
    DeeLength = 20.0; // … recess depth
    DeeRodCyl = 28.0; // … overall rod length
    AdapterOD = BobbinRodOD + 2*WallThick;
    AdapterOAH = 1.5*AdapterOD;
    AdapterOAL = AdapterOD + DeeLength;
    //—–
    // Define things
    // Surplus-deal 6 mm rod with a lengthy flat on one end
    module DeeRod() {
    union() {
    intersection() {
    xcyl(DeeLength + Protrusion,d=BobbinRodOD,anchor=RIGHT);
    down((BobbinRodOD – BobbinRodDee)/2)
    cuboid([DeeLength + Protrusion,BobbinRodOD,BobbinRodDee],anchor=RIGHT);
    }
    xcyl(DeeRodCyl,d=BobbinRodOD,anchor=LEFT);
    }
    }
    module Adapter() {
    difference() {
    union() {
    cyl(AdapterOAH,d=AdapterOD,rounding=WallThick/2);
    xcyl(AdapterOAL – AdapterOD/2,d=AdapterOD,anchor=LEFT);
    }
    zcyl(AdapterOAH + 2*Protrusion,d=SpoolRodOD);
    cuboid([AdapterOD+2*Protrusion,Kerf,AdapterOAH+2*Protrusion]);
    right(AdapterOD/2 + DeeLength + Protrusion)
    DeeRod();
    }
    }
    //—–
    // Build it
    if (Layout == "Show") {
    Adapter();
    color("Green",0.5)
    zcyl(3*AdapterOAH,d=SpoolRodOD);
    color("Magenta",0.5)
    right(AdapterOD/2 + DeeLength + Protrusion)
    DeeRod();
    }
    if (Layout == "Build")
    up(AdapterOD/2 + DeeLength)
    yrot(90)
    Adapter();

  • Custom 45° Triangle Quilting Ruler

    Custom 45° Triangle Quilting Ruler

    Mary’s current quilt project has a corner design with an essentially infinite number of 45° triangles, which another custom ruler will simplify:

    45° Quilting Ruler - finished
    45° Quilting Ruler – finished

    That’s the end result of several iterations, proceeding from doodles to sketches to increasingly accurate laser-cut prototypes:

    45° Quilting Ruler - prototypes
    45° Quilting Ruler – prototypes

    A “ruler” in quilting parlance is a thing guiding the sewing machine’s “ruler foot” across the fabric (or, for sit-down machines, the fabric under the foot) in specific directions:

    45° Quilting Ruler - in use
    45° Quilting Ruler – in use

    That’s a practice quilt on scrap fabric: quilters need prototypes, too!

    The foot is 0.5 inch OD, within a reasonable tolerance, which accounts for the slot width in the ruler. It’s also intended to run against 1/4 inch thick rulers, which accounts for the thickness of that slab of acrylic.

    The engraved lines & arcs are on the bottom of the ruler to eliminate parallax errors against the fabric, so the bottom is upward and the text is mirrored for the laser:

    45° Quilting Ruler - cutting
    45° Quilting Ruler – cutting

    Although fluorescent green acrylic may have higher visibility, clear seems adequate for the fabric in question:

    45° Quilting Ruler - colored fabric
    45° Quilting Ruler – colored fabric

    I very carefully trimmed the arcs against the ruler outline using LightBurn’s Cut Shapes, which turned out to be a Bad Idea™, because the high-current pulse as the laser fires causes a visible puncture wound at the still-to-be-cut edge:

    45° Quilting Ruler - edge damage
    45° Quilting Ruler – edge damage

    Those are not straight lines and the plastic isn’t bent!

    A closer look:

    45° Quilting Ruler - edge damage - detail
    45° Quilting Ruler – edge damage – detail

    The arcs without wounds started from their other end and stopped at the edge, which is perfectly fine.

    The wounds are unsightly, not structural, but the next time around I’ll extend the markings a millimeter beyond the edges into the scrap material.

    The overall design looks busier than it is, because I put different features on different layers in case they needed different settings:

    45 Degree Quilting Ruler - LightBurn layout
    45 Degree Quilting Ruler – LightBurn layout

    The LightBurn SVG layout as a GitHub Gist:

    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  • Generator Cover Screw Knob

    Generator Cover Screw Knob

    The latches holding the side cover of the portable generator in place work well enough that I never tighten the cover screws, but sometimes one will vibrate itself into place and require less than one turn of a screwdriver to release. Given that I put a knob on the air filter screw, a pair of knobs on the side cover screws makes sense:

    Generator Cover Screw Knob - installed
    Generator Cover Screw Knob – installed

    Those are custom screws! The narrow neck keeps them captive in the cover, which is a Good Thing™.

    These knobs obviously descend from the air filter knob, with less knurling and a short shaft to clear the recess in the cover:

    Generator Cover Screw Knob - solid model
    Generator Cover Screw Knob – solid model

    Unlike the air filter knob, the double-sided tape gluing these to their screws isn’t continually compressed, so the knobs may eventually shake off. Should that happen, I’ll deploy epoxy.

    The OpenSCAD source code:

    // Generator cover screw knob
    // Ed Nisley - KE4ZNU
    // 2026-03-13
    
    include <BOSL2/std.scad>
    
    /* [Hidden] */
    
    // Screw head dome
    
    HeadHeight = 2.0;
    HeadOD = 10.8;
    
    DomeRadius = (HeadHeight^2 + (HeadOD^2)/4) / (2*HeadHeight);
    echo(DomeRadius=DomeRadius);
    
    KnobOD = 15.0;
    KnobLength = 10.0;
    
    ShaftOD = HeadOD;
    ShaftLength = 7.0;
    
    RimFudge = 0.3;   // ensures a printable edge
    
    // Build it
    
    difference() {
      cyl(h=KnobLength, r=KnobOD/2,anchor=BOTTOM,texture="trunc_pyramids",tex_size=[3.0,KnobLength/3]) position(TOP)
        cyl(ShaftLength,d=ShaftOD,anchor=BOTTOM);
      up(KnobLength + ShaftLength - HeadHeight + RimFudge)
        spheroid(r=DomeRadius,circum=true,style="icosa",anchor=BOTTOM);
    }
    
    
  • Wobbly Clothes Rack Repair

    Wobbly Clothes Rack Repair

    A clothes rack Mary intended use with some work-in-progress quilts seemed entirely too wobbly for the purpose, so I tried tightening its screws. This did not go well, as some of the threaded inserts sunk into the vertical bars spun freely and, with a bit of persuasion, pulled straight out of their sockets:

    Clothes rack screws - threaded insert penetrating oil
    Clothes rack screws – threaded insert penetrating oil

    The reddish fluid is Kroil penetrating oil I hoped would free the screws from the corrosion locking them into the inserts. After an overnight soak, they still required force majeure:

    Clothes rack screws - threaded insert in vise
    Clothes rack screws – threaded insert in vise

    The two inserts on the left came from the top of the rack and the other two from the bottom:

    Clothes rack screws - threaded insert corrosion
    Clothes rack screws – threaded insert corrosion

    Similar inserts have a hex drive recess and, because these are for 1/4-20 screws, I expected an inch size hex key. Nope, they want a hard metric 6 mm:

    Clothes rack screws - threaded insert reformed
    Clothes rack screws – threaded insert reformed

    I cleaned up the corroded inserts by the simple expedient of tapping them firmly onto the 6 mm wrench held in the vise:

    Clothes rack screws - threaded insert hex reforming
    Clothes rack screws – threaded insert hex reforming

    The crud around the bottom fell out of previous contestants during their reformation.

    I considered epoxying the inserts in place, but settled for tucking a thick paper shim into each hole:

    Clothes rack screws - threaded insert shim
    Clothes rack screws – threaded insert shim

    They’re entirely snug right now and, should they work loose, I’ll coat the hole with epoxy, roll up another shim, screw the insert in place, await curing, then declare victory and hope nobody must ever remove them.

    The 1/4-20 screws in the top member sit deep in recesses that surely had decorative wood plugs when the rack left the factory. Alas, they’re long gone, which may have let water / moisture corrode the screws + inserts . I’m not much good for “decorative” items, so this must suffice:

    Clothes Rack Screw Covers - solid model
    Clothes Rack Screw Covers – solid model

    A snippet of double-sided tape on one side of the hole keeps them in place:

    Clothes rack screws - cover installed
    Clothes rack screws – cover installed

    They look better in person …

    The trivial OpenSCAD source code:

    // Clothes rack screw cover
    // Ed Nisley - KE4ZNU
    // 2026-03-13
    
    include <BOSL2/std.scad>
    
    /* [Hidden] */
    
    NumSides = 4*3*3*4;
    $fn=NumSides;
    
    //----------
    // Build it
    //  … with magic numbers from the rack
    
    cyl(3.0,d=16.7,chamfer1=1.0,anchor=BOTTOM) position(TOP)
      cyl(6.0,d=12.9,chamfer2=1.0,anchor=BOTTOM);