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

Prusa Mk 4 3D printer with MMU3 feeder

  • Manual Drive Gear for NRC Rotary Positioner

    Manual Drive Gear for NRC Rotary Positioner

    While aligning the Sherline’s laser aligner, an old rotary positioner, apparently made by NRC, emerged from the Box o’ Optical Lab equipment:

    Rotary positioner - as found
    Rotary positioner – as found

    That’s not quite “as found”, because it came festooned with the remains of an obviously lab-built Peltier-cooled laser (?) diode fixture:

    Rotary positioner - Peltier diode fixture
    Rotary positioner – Peltier diode fixture

    The positioner sported an obviously aftermarket tapped hole in the side, presumably for mounting to a support:

    Rotary positioner - tapped mounting hole
    Rotary positioner – tapped mounting hole

    The knob was apparently intended for fine angle adjustment, but it spun freely. Loosening another setscrew on the side released its well-worn parts:

    Rotary positioner - drive gear - OEM knob
    Rotary positioner – drive gear – OEM knob

    It’s not clear what the brown ring did, back when it did something, but there were no signs of stripped-off teeth or other debris in the recess; it is a very sloppy fit on the pin holding the knob. The knob may have had a compliant surface engaging the top of the ring, made with a long-since fossilized substance.

    I figured this was a great excuse to renew my acquaintance with the BOSL2 gear generator:

    Rotary positioner - drive gear - solid model
    Rotary positioner – drive gear – solid model

    It turns out the rotary ring has triangles, not gear teeth:

    Rotary positioner - tooth detail
    Rotary positioner – tooth detail

    However, setting the gear tooth pressure angle to 45° produces a reasonable triangle:

    Rotary positioner - drive gear - solid model - end view
    Rotary positioner – drive gear – solid model – end view

    Even so, getting a functional knob required many iterations, primarily because I can’t measure any of the details and had to figure the fit by cut-and-try:

    Rotary positioner - drive gear - gallery
    Rotary positioner – drive gear – gallery

    The little white dots were an excuse to use the MMU3 for multi-material printing, because why not.

    In truth, the knob doesn’t work particularly well, as the forces from the triangular teeth on the rotary ring tend to jam the knob against its pin. The knob might work better with splines driving a squishy TPU tire riding the crests of the rotary ring teeth than a real gear. Perhaps that’s what the original brown ring did before it fossilized.

    For now, the positioner returns to the Box o’ Optics Lab Stuff, because it’s the wrong hammer for the Sherline’s laser aligner. It may emerge for a future project, when I’ll have more motivation to build a functional knob.

    The OpenSCAD source code as a GitHub Gist:

    // Rotary positioner drive gear with knob
    // Ed Nisley – KE4ZNU
    include <BOSL2/std.scad>
    include <BOSL2/gears.scad>
    NumSides = 4*3*4;
    $fn=NumSides;
    Protrusion = 0.1; // make holes end cleanly
    HoleWindage = 0.2; // make holes large enough to fit
    ShaftOD = 5.5;
    Knurling = "trunc_ribs";
    difference() {
    cyl(h=9.5,d=16.0,anchor=BOTTOM,texture=Knurling,tex_size=[3.0,4.0]) position(TOP)
    cyl(h=1.8,d=15.2,circum=false,anchor=BOTTOM) position(TOP)
    color("Green")
    scale([1.03,1.03,1]) // nominal to physical hackery
    spur_gear(diam_pitch=60.0,teeth=24,pressure_angle=45,thickness=4.7,anchor=BOTTOM);
    down(Protrusion) {
    cyl(h=1.6,d=8.2 + HoleWindage,circum=true,anchor=BOTTOM);
    cyl(h=25,d=ShaftOD + HoleWindage,circum=true,anchor=BOTTOM);
    }
    }
    color("Red")
    right((8.2 + 16.0)/4)
    cyl(h=0.6,d=2.0,circum=false,anchor=BOTTOM);

  • Photo Backdrop: Crossbar Improvement

    Photo Backdrop: Crossbar Improvement

    The instructions for the Photo Backdrop frame (upon which we hang Mary’s quilts for photos) suggest the crossbar fits on like this:

    Photo Backdrop - OEM crossbar installation
    Photo Backdrop – OEM crossbar installation

    The slot in the bottom is wider than the M10 stud, so the crossbar tends to flop around while assembling it overhead. I immediately replaced the wingnut with a chunky knob for better griptitude, but was never happy with how poorly the whole thing fit together.

    This is dramatically better:

    Photo Backdrop - fitting installed
    Photo Backdrop – fitting installed

    The crossbar is upside-down from the OEM instructions, but the bottom of the gray plug holds the tube firmly to the tripod while the nut seats firmly on the plug’s flat top:

    Photo Backdrop - fitting top view
    Photo Backdrop – fitting top view

    A snippet of 3M 300LSE adhesive sheet holds the plug in place, so that’s one less thing to fiddle with on each end.

    The solid model holds no surprises:

    Photo Backdrop Fittings - solid model
    Photo Backdrop Fittings – solid model

    Of course, it builds with the flat end downward.

    The OpenSCAD source code as a GitHub Gist:

    // Photo backdrop fitting
    // Ed Nisley – KE4ZNU
    // 2026-07-01
    include <BOSL2/std.scad>
    Layout = "Show"; // [Build,Show]
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    HoleWindage = 0.2;
    Protrusion = 0.01;
    NumSides = 3*2*4;
    Clearance = 0.3;
    $fn=NumSides;
    Tube = [23.0 – HoleWindage,1*INCH,100.0]; // arbitrary length
    Aperture = [24.0,15.0,100.0]; // oblong nut hole, arbitrary Z
    Washer = [10.5,20.0,1.5]; // M10
    //—–
    // Define things
    module Fitting() {
    difference() {
    union() {
    intersection() {
    xcyl(Tube[LENGTH],d=Tube[ID]);
    cuboid(Aperture,rounding=Aperture.y/2,edges="Z");
    }
    cuboid([Aperture.x,Aperture.y,Tube[OD]/2],rounding=Aperture.y/2,edges="Z",anchor=BOTTOM);
    }
    cyl(Aperture.z,d=Washer[ID]);
    }
    }
    //—–
    // Build things
    if (Layout == "Show") {
    Fitting();
    }
    if (Layout == "Build") {
    up(Tube[OD]/2)
    xrot(180)
    Fitting();
    }

  • Hose Nozzle Flow Restrictors

    Hose Nozzle Flow Restrictors

    Mary wanted less pressure in the spray while watering her plants and I suggested replacing the nozzle’s washer with a flow restrictor:

    Hose Nozzle Flow Restrictors - assembled
    Hose Nozzle Flow Restrictors – assembled

    The 3D-printed TPU base is squishy enough to act as a hose washer:

    Hose Nozzle Flow Restrictor Base - solid model
    Hose Nozzle Flow Restrictor Base – solid model

    A 1.5 mm thick acrylic orifice plate snapped into the opening takes advantage of the laser cutter’s precision:

    Hose Nozzle Flow Restrictors - LightBurn layout
    Hose Nozzle Flow Restrictors – LightBurn layout

    For lack of anything smarter, the holes have areas that are powers-of-two smaller than the nozzle’s 14.2 mm = 158 mm² internal passage: the hole labeled 8 is 158/256 mm² = 0.62 mm² → 0.9 mm diameter.

    Rather than figuring each hole’s diameter, just divide the previous diameter by √2 or rescale it by 100%/√2, which LightBurn can evaluate directly in its Numeric Edits fields. The as-cut holes are larger than their nominal size by about 0.1 mm, but any errors that might cause are definitely in the nature of fine tuning while watering the plants.

    The nozzle’s Shower pattern (on the left in the picture) has a 6.8 L/minute = 110 ml/s flow through an ordinary garden hose washer. The four smallest aperture plates produced these flows:

    Hose Nozzle Flow Restrictors - flow vs dia
    Hose Nozzle Flow Restrictors – flow vs dia

    The flow should scale with the square of the aperture diameter, which I could bully those points into suggesting, but the measurement accuracy produced by filling a gallon jug while tapping my phone’s stopwatch doesn’t justify anything fancier.

    The two smallest apertures reduce the Shower pattern to a very gentle spray requiring far too long to put enough water on the plants. Mary now uses an old plastic sprinkler head with enough holes to produce a dense spray with very little force, with the flow set by fifty feet of PEX pipe running across the width of the house from the town water inlet to the hose bib.

    It was a fun exercise and I learned a little more about printing TPU and fitting acrylic parts therein:

    Hose Nozzle Flow Restrictors - prototypes
    Hose Nozzle Flow Restrictors – prototypes
  • Dripworks Mainline Leak: Repair Under Field Conditions

    Dripworks Mainline Leak: Repair Under Field Conditions

    A large gooey puddle helped isolate a leak in the Dripworks main line pipe running the length of Mary’s Vassar Farms plot:

    Dripworks Mainline clamp - injured hose joint
    Dripworks Mainline clamp – injured hose joint

    Much of the tubing between the transfer barb in the buried pipe and the cross coupling lies on the surface, where it’s subject to missteps. This being just a few feet inside the garden gate, it’s no surprise enough missteps caused the barb to no longer seal properly.

    So I pulled the barb out of the pipe and deployed the backup pipe clamp I made after fixing a previous mishap:

    Dripworks Mainline clamp - installed
    Dripworks Mainline clamp – installed

    You’re supposed to wrap silicone tape while keeping its surface clean, which is obviously impossible in a hole rapidly filling with water draining from the plumbing but the clamp presses the tape firmly against the pipe and seals the leak.

    There is, I regret to say, an 8-32 stainless steel washer lost somewhere deep in the muck.

    I punched a new barb into the pipe with slightly longer tubing to the cross fitting, in the hope it’ll be more resilient.

    Another clamp with its silicone tape snippet stands ready for duty:

    Dripworks Mainline Pipe Clamp - assembled
    Dripworks Mainline Pipe Clamp – assembled

    For the record, the Micromark Cutoff Saw has a 3 mm offset between the side of the vise and the left edge of the blade:

    Micromark abrasive cutoff - work offset
    Micromark abrasive cutoff – work offset

    I still lack a Round Tuit for improving that vise.

  • BOB Yak Trailer: Fender Front Mount

    BOB Yak Trailer: Fender Front Mount

    I eventually tracked a distressingly loud rattle from the BOB Yak trailer to a fender mount failure:

    BOB Yak Trailer fender front mount - aluminum fatigue
    BOB Yak Trailer fender front mount – aluminum fatigue

    The screw clamped the round aluminum fender between two flat washers (the other of which has been touring the workbench). The hole in the aluminum started as a screw slot and eventually fretted away around the edge of the washers, leaving a trapped fragment to fall out as I loosened the screw.

    Well, this mount lasted a decade longer than the wire mount at the top of the fender, so there’s that.

    As before, a bit of math conjures a chunky mount from the vasty digital deep:

    Fender front mount - solid model - Show view
    Fender front mount – solid model – Show view

    The first iteration didn’t have the hole for the threaded insert angled downward at 10°, but it’s easier to make better measurements with a “pretty close” prototype. I’m reasonably sure the angle is a glitch due to hand-brazing the frame tubes, but we’ll never know.

    The inner plate angles to match the insert, thus keeping the screw & washer perpendicular to the surface:

    Fender front mount - solid model - Mounts view
    Fender front mount – solid model – Mounts view

    A brim around that chip of plastic ensures a good grip on the platform:

    BOB Yak Trailer - fender front mount - PrusaSlicer preview
    BOB Yak Trailer – fender front mount – PrusaSlicer preview

    I suppose rounding the corners would make it prettier:

    BOB Yak Trailer fender front mount - inner plate
    BOB Yak Trailer fender front mount – inner plate

    The original screw was slightly too short, so that’s a shiny replacement from the Drawer o’ Random M5 Screws. If I ever have occasion to go in there again, I’ll use a button head screw, although there’s certainly enough clearance:

    BOB Yak Trailer fender front mount - tire clearance
    BOB Yak Trailer fender front mount – tire clearance

    From the top, the gray PETG-CF looks like it grew there:

    BOB Yak Trailer fender front mount - installed
    BOB Yak Trailer fender front mount – installed

    I figured the mount’s radius by feeding measurements into the chord equation and assuming the overall curve is circular; the radius came out slightly too large, which likely won’t make much difference.

    The OpenSCAD source code as a GitHub Gist:

    // BOB Yak Trailer – fender front mount
    // Ed Nisley – KE4ZNU
    // 2026-06-15
    include <BOSL2/std.scad>
    Layout = "Show"; // [Build,Show,Frame,Fender,OuterMount,InnerMount,Mounts]
    /* [Hidden] */
    ID = 0;
    OD = 1;
    LENGTH = 2;
    HoleWindage = 0.2;
    Protrusion = 0.01;
    NumSides = 4*3*2*4;
    Gap = 5.0/2;
    $fn=NumSides;
    WallThick = 5.0;
    Washer = [6.0,16.0,1.5]; // M5 fender washer
    Rivnut = [5.0,10.3,1.5]; // M5 rivnut in frame
    FrameOD = 16.1; // trailer frame tubing
    FrameAngle = 10;
    FenderOA = [52,440,1.5]; // minor major thickness
    BlockOA = [0,40.0,1.25*Washer[OD]];
    //—–
    // Define things
    // Relevant part of the trailer frame
    // origin at center of rivnut
    module Frame() {
    yrot(FrameAngle)
    union() {
    left(Rivnut[LENGTH])
    ycyl(2*BlockOA.y,d=FrameOD,anchor=RIGHT);
    xcyl(FrameOD/2,d=Rivnut[OD],anchor=RIGHT);
    left(Protrusion)
    xcyl(10,d=Rivnut[OD],anchor=LEFT);
    }
    }
    module OuterFender() {
    torus(od=FenderOA[OD],d_min=FenderOA[ID],orient=FRONT,anchor=LEFT);
    }
    module FullFender() {
    difference() {
    OuterFender();
    right(FenderOA[LENGTH]) // make it a cup
    torus(od=FenderOA[OD] – 2*FenderOA[LENGTH],d_min=FenderOA[ID] – 2*FenderOA[LENGTH],
    orient=FRONT,anchor=LEFT);
    right(FenderOA[ID]/2) // remove inner half
    ycyl(2*BlockOA.y,d=FenderOA[OD] – FenderOA[ID],anchor=LEFT);
    down(2*Washer[OD]) // remove bottom part
    cuboid(2*[FenderOA[OD],FenderOA[OD],FenderOA[OD]],anchor=TOP+LEFT);
    }
    }
    module OuterMount() {
    difference() {
    right(FenderOA[ID]/2)
    cuboid([FrameOD/3 + Washer[LENGTH] + FenderOA[ID]/2,BlockOA.y,BlockOA.z],
    rounding=1.0,anchor=RIGHT);
    Frame();
    OuterFender();
    }
    }
    module InnerMount() {
    difference() {
    render()
    intersection() {
    yrot(FrameAngle)
    cuboid([WallThick + FenderOA[LENGTH],BlockOA.y,BlockOA.z],anchor=LEFT);
    right(FenderOA[LENGTH])
    OuterFender();
    }
    yrot(FrameAngle)
    xcyl(FenderOA[ID],d=Washer[ID],anchor=LEFT);
    }
    }
    //—–
    // Build it
    if (Layout == "Frame") {
    Frame();
    }
    if (Layout == "Fender") {
    FullFender();
    }
    if (Layout == "OuterMount") {
    OuterMount();
    }
    if (Layout == "InnerMount") {
    InnerMount();
    }
    if (Layout == "Mounts") {
    OuterMount();
    InnerMount();
    }
    if (Layout == "Show") {
    OuterMount();
    InnerMount();
    color("Gray",0.6) {
    Frame();
    FullFender();
    }
    }
    if (Layout == "Build") {
    up(BlockOA.z/2) left(FenderOA[ID]/4)
    OuterMount();
    up(BlockOA.z/2)
    xrot(180)
    yrot(-FrameAngle)
    InnerMount();
    }
  • Earplug Case

    Earplug Case

    A no-assembly-needed earplug case from Printables will be more easily found in Mary’s purse than the previous small bag:

    Earplug case
    Earplug case

    That’s the “grippy bits” version of the model, which really is easier to open than the straight-sided version.

    I printed a few more, loaded them with earplugs, and put them where they may come in handy. In retrospect, I should have used clear PETG to show off the retina-burn plugs.

    Living in the future is great!

  • Outlet Strip Bench Mount

    Outlet Strip Bench Mount

    A spate of tidying-up led to mounting an outlet strip along the back of a bench:

    Outlet Bench Mount - installed
    Outlet Bench Mount – installed

    Rather than drill holes into the top of the bench for those screws, they fit into M4 brass inserts heat-staked into the brackets:

    Outlet Bench Mount - show view
    Outlet Bench Mount – show view

    The holes for those inserts aren’t centered side-to-side on the brackets, because the screw holes aren’t centered on the bent-steel angles forming the outlet strip endplates.

    The bottom arm on the brackets probably isn’t necessary, but they kept the outlet strip from crawling away while I match-drilled two holes for the screws into the side of the benchtop.

    For obvious reasons, the brackets print on their sides:

    Outlet Bench Mount - build view
    Outlet Bench Mount – build view

    Another outlet strip from a different manufacturer is, of course, different, but changing three parameters in the OpenSCAD program summons a different bracket from the vasty digital deep:

    Outlet Bench Mount - different brand
    Outlet Bench Mount – different brand

    Parametric modeling and a 3D printer are exactly the right hammers for the job …

    The OpenSCAD source code as a GitHub Gist:

    // Shower soap dish
    // Ed Nisley – KE4ZNU
    // 2026-06-04
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build]
    /* [Hidden] */
    HoleWindage = 0.2;
    Protrusion = 0.1;
    NumSides = 3*3*4;
    Gap = 10.0/2;
    $fn=NumSides;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    BenchThick = 21.0; // workbench top
    ScrewOD = 4.0; // into edge of bench
    Insert = [4.0,5.5,10.0]; // robust M4 insert
    WallThick = 10.0;
    BaseThick = 10.0;
    OutletBase = [15.0,40.0];
    HoleOffset = 6.5; // from outside edge of bracket
    HoleOC = 24.0;
    MountOA = [OutletBase.x,OutletBase.y,BenchThick + Insert[LENGTH] + 1.0 + BaseThick];
    //———-
    // Build it
    module Mount() {
    difference() {
    cuboid(MountOA,rounding=1.0,anchor=BOTTOM + BACK);
    up(BaseThick)
    fwd(WallThick)
    cuboid([2*MountOA.x,MountOA.y,BenchThick],anchor=BOTTOM + BACK);
    up(BaseThick + BenchThick/2) back(Protrusion)
    ycyl(OutletBase.y,d=ScrewOD,circum=true,$fn=6,anchor=BACK);
    for (j=[-1,1])
    fwd(MountOA.y/2 + j*HoleOC/2)
    right(HoleOffset – MountOA.x/2)
    up(MountOA.z + Protrusion)
    cyl(Insert[LENGTH],d=Insert[OD],circum=true,$fn=6,anchor=TOP);
    }
    }
    //———-
    // Build it
    if (Layout == "Show") {
    left(Gap + MountOA.x/2)
    Mount();
    right(Gap + MountOA.x/2)
    xflip() Mount(); // mirror for the other end of the outlet strip
    }
    if (Layout == "Build") {
    left(MountOA.z/2)
    up(MountOA.x/2)
    yrot(90)
    Mount();
    fwd(1.5*MountOA.y)
    left(MountOA.z/2 – BenchThick/2 – Insert[LENGTH]/2)
    zrot(180)
    up(MountOA.x/2)
    yrot(-90)
    xflip() Mount(); // mirror for the other end of the outlet strip
    }