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

Using and tweaking a Makergear M2 3D printer

  • JYE Tech DSO150 Oscilloscope: Battery Power

    With the DSO150 scope running, I printed Geoff’s DSO150 case + battery holder from Thingiverse, added a few bits & pieces from the heap, and came up with a completely portable scope:

    DSO150 battery hack - first light
    DSO150 battery hack – first light

    The only scope mod consists of embedding a JST-ish connector in the back panel:

    DSO150 battery hack - rear panel connector
    DSO150 battery hack – rear panel connector

    Then soldering it to the battery pads and applying generous hot-melt glue blobs:

    DSO150 battery hack - PCB power
    DSO150 battery hack – PCB power

    Add a scrap 18650 Li-Ion cell, a regulated boost converter, and a switch:

    DSO150 battery hack - interior
    DSO150 battery hack – interior

    The switch is directly below the DSO150 BNC connector to get a little protection for its handle, which would otherwise stick out in harm’s way. This being an afterthought, I drilled the switch hole, rather than modify the solid model.

    Some testing with a bench supply showed that the DSO150 will not operate correctly from the voltages produced by a pair of lithium cells, despite what you’d think from looking at the case. Below 8 V, the internally generated negative supply becomes larger than the positive supply, so the 0 V point isn’t properly centered and the scope loses headroom for large signals; monitoring the internal 3.3 V test signal makes the problem painfully obvious.

    More color commentary from my summary email:

    • Combining a case from Thingiverse with a Li-Ion cell and a regulated boost converter produces a portable scope.
    • The PCB has provision for battery input, so I drilled / filed a square hole for a teeny JST-ish connector on the back panel, secured it with a blob of hot melt glue, and globbed the wires onto the PCB battery pads.
    • The boost converter draws about 400 mA from the cell, so a 2500-ish mA·h cell should last Long Enough™. This is a scrap cell from the recycle box and gave out after maybe four hours.
    • It idles at 8 mA, so I drilled a hole in the back of the case for a toggle switch disconnecting the battery; you’d want the hole in the solid model. Perhaps a better converter would have lower idle current; you’d never be able to tell from the eBay descriptions.
    • Aaaaand it switches around 200 kHz under load, just barely beyond the scope bandwidth. It doesn’t add much noise to the signal, at least with a 50 Ω terminator jammed in the BNC, but the square-wave “cal” output looks awful at 50 mV/div; a real scope shows even more noise. I assume the noise comes directly from the logic supply; with luck, the DSO150’s analog circuitry has Good Enough™ filtering.
    • Which might not matter for logic-level and moderate analog signals, of course, which is the whole point of the DSO150.
    • Conspicuous by their absence: a Li-Ion cell protection PCB and any way to recharge the poor thing …

    I’ve occasionally wanted a portable scope and now I have one!

  • Sony NP-BX1 Battery Holder: SMT Pogo Pin Contacts

    The original camera battery test fixtures used contact pins conjured from hulking gold-plated connector pins and coil springs:

    Canon NB-6L holder - contact pin detail
    Canon NB-6L holder – contact pin detail

    The Sony HDR-AS30V camera chewed up and spat out a handful of batteries, all tested in the NP-BX1 test fixture:

    NP-BX1 Holder - show layout
    NP-BX1 Holder – show layout

    Nowadays, SMT pogo pins produce a much more compact holder, so I figured I could put all those batteries to good use:

    NP-BX1 Holder - SMT pogo pins
    NP-BX1 Holder – SMT pogo pins

    That’s the long-suffering astable multivibrator, still soldered to its CR123A holder.

    Obviously, the battery holder should grow ears to anchor the 14 AWG copper posts and would look better in black PETG:

    NP-BX1 Battery Holder - 1.5mm pins - solid model
    NP-BX1 Battery Holder – 1.5mm pins – solid model

    The battery lead wires get soldered to the ends of the pogo pins and are recessed into the slot in the end of the fixture. I used clear epoxy to anchor everything in place.

    Fits perfectly and works fine!

    The OpenSCAD source code as a GitHub Gist:

    // Holder for Sony NP-BX1 Li-Ion battery
    // Ed Nisley KE4ZNU January 2013
    // 2018-11-15 Adapted for wire leads from 1.5 mm test pins, added upright wire bases
    // Layout options
    Layout = "Show"; // Show Build Fit Case Lid Pins
    //- Extrusion parameters – must match reality!
    // Print with +2 shells and 3 solid layers
    ThreadThick = 0.25;
    ThreadWidth = 0.35;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    BuildOffset = 3.0; // clearance for build layout
    Gap = 2.0; // separation for Fit parts
    //- Battery dimensions – rationalized from several samples
    // Coordinate origin at battery contact face with key openings below contacts
    Battery = [43.0,30.0,9.5]; // X = length, Y = width, Z = thickness
    Contacts = [[-0.75,6.0,6.2],[-0.75,16.0,6.2]]; // relative to battery edge, front, and bottom
    KeyBlocks = [[1.75,3.70,2.90],[1.75,3.60,2.90]]; // recesses in battery face set X position
    //- Pin dimensions
    ID = 0;
    OD = 1;
    LENGTH = 2;
    PinShank = [1.5,2.0,6.5]; // shank, flange, compressed length
    PinFlange = [1.5,2.0,0.5]; // flange, length included in PinShank
    PinTip = [0.9,0.9,2.5]; // extended spring-loaded tip
    PinChannel = PinFlange[LENGTH] + 0.5; // cut behind flange for solder overflow
    PinRecess = 3.0; // recess behind pin flange end for epoxy fill
    echo(str("Contact tip dia: ",PinTip[OD]));
    echo(str(" .. shank dia: ",PinShank[ID]));
    OverTravel = 0.5; // space beyond battery face at X origin
    //- Holder dimensions
    GuideRadius = ThreadWidth; // friction fit ridges
    GuideOffset = 7; // from compartment corners
    WallThick = 4*ThreadWidth; // holder sidewalls
    BaseThick = 6*ThreadThick; // bottom of holder to bottom of battery
    TopThick = 6*ThreadThick; // top of battery to top of holder
    ThumbRadius = 10.0; // thumb opening at end of battery
    CornerRadius = 3*ThreadThick; // nice corner rounding
    CaseSize = [Battery.x + PinShank[LENGTH] + OverTravel + PinRecess + GuideRadius + WallThick,
    Battery.y + 2*WallThick + 2*GuideRadius,
    Battery.z + BaseThick + TopThick];
    CaseOffset = [-(PinShank[LENGTH] + OverTravel + PinRecess),-(WallThick + GuideRadius),0]; // position around battery
    LidOverhang = 2.0; // over top of battery for retention
    LidSize = [-CaseOffset.x + LidOverhang,CaseSize.y,TopThick];
    LidOffset = [0.0,CaseOffset.y,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);
    }
    //——————-
    //– Guides for tighter friction fit
    module Guides() {
    translate([GuideOffset,-GuideRadius,0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([GuideOffset,(Battery.y + GuideRadius),0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x – GuideOffset),-GuideRadius,0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x – GuideOffset),(Battery.y + GuideRadius),0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x + GuideRadius),GuideOffset/2,0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    translate([(Battery.x + GuideRadius),(Battery.y – GuideOffset/2),0])
    PolyCyl(2*GuideRadius,(Battery.z – Protrusion),4);
    }
    //– Contact pins
    // Rotated to put them in their natural oriention
    // Aligned to put tip base / end of shank at Overtravel limit
    module PinShape() {
    translate([-(PinShank[LENGTH] + OverTravel),0,0])
    rotate([0,90,0])
    rotate(180/6)
    union() {
    PolyCyl(PinTip[OD],PinShank[LENGTH] + PinTip[LENGTH],6);
    PolyCyl(PinShank[ID],PinShank[LENGTH] + Protrusion,6); // slight extension for clean cuts
    PolyCyl(PinFlange[OD],PinFlange[LENGTH],6);
    }
    }
    // Position pins to put end of shank at battery face
    // Add wire exit channel between pins
    // Does not include recess access
    module PinAssembly() {
    union() {
    for (p = Contacts)
    translate([0,p.y,p.z])
    PinShape();
    translate([-(PinShank[LENGTH] + OverTravel) + PinChannel/2,
    (Contacts[1].y + Contacts[0].y)/2,
    Contacts[0].z])
    cube([PinChannel,(Contacts[1].y – Contacts[0].y),PinFlange[OD]],center=true);
    }
    }
    //– Case with origin at battery corner
    module Case() {
    difference() {
    union() {
    difference() {
    translate([(CaseSize.x/2 + CaseOffset.x), // basic case shape
    (CaseSize.y/2 + CaseOffset.y),
    (CaseSize.z/2 – BaseThick)])
    hull()
    for (i=[-1,1], j=[-1,1], k=[-1,1])
    translate([i*(CaseSize.x/2 – CornerRadius),
    j*(CaseSize.y/2 – CornerRadius),
    k*(CaseSize.z/2 – CornerRadius)])
    sphere(r=CornerRadius,$fn=8);
    translate([-OverTravel,-GuideRadius,0])
    cube([(Battery.x + GuideRadius + OverTravel),
    (Battery.y + 2*GuideRadius),
    (Battery.z + Protrusion)]); // battery space
    }
    Guides(); // improve friction fit
    translate([-OverTravel,-GuideRadius,0]) // battery keying blocks
    cube(KeyBlocks[0] + [OverTravel,GuideRadius,0],center=false);
    translate([-OverTravel,(Battery.y – KeyBlocks[1].y),0])
    cube(KeyBlocks[1] + [OverTravel,GuideRadius,0],center=false);
    }
    translate([(-OverTravel), // battery top access
    (CaseOffset.y – Protrusion),
    Battery.z])
    cube([CaseSize.x,(CaseSize.y + 2*Protrusion),(TopThick + Protrusion)]);
    translate([(CaseOffset.x – Protrusion), // battery insertion allowance
    (CaseOffset.y – Protrusion),
    Battery.z])
    cube([(CaseSize.x + 2*Protrusion),(CaseSize.y + 2*Protrusion),(TopThick + Protrusion)]);
    translate([(Battery.x – Protrusion), // remove thumb notch
    (CaseSize.y/2 + CaseOffset.y),
    (ThumbRadius)])
    rotate([90,0,0])
    rotate([0,90,0])
    cylinder(r=ThumbRadius,
    h=(WallThick + GuideRadius + 2*Protrusion),
    $fn=22);
    PinAssembly();
    translate([CaseOffset.x + PinRecess/2 + Protrusion/2,(Contacts[1].y + Contacts[0].y)/2,Contacts[0].z])
    cube([PinRecess + Protrusion,
    (Contacts[1].y – Contacts[0].y + PinFlange[OD]),
    2*PinFlange[OD]],center=true);
    }
    }
    // Lid position offset to match case
    module Lid() {
    translate([-LidSize.x/2 + LidOffset.x + LidOverhang,LidSize.y/2 + LidOffset.y,0])
    difference() {
    hull()
    for (i=[-1,1], j=[-1,1], k=[-1,1])
    translate([i*(LidSize.x/2 – CornerRadius),
    j*(LidSize.y/2 – CornerRadius),
    k*(LidSize.z – CornerRadius)]) // double thickness for flat bottom
    sphere(r=CornerRadius,$fn=8);
    translate([0,0,-LidSize.z/2])
    cube([(LidSize.x + 2*Protrusion),(LidSize.y + 2*Protrusion),LidSize.z],center=true);
    cube([LidSize.x/4,0.75*LidSize.y,4*ThreadThick],center=true); // epoxy recess
    }
    }
    //——————-
    // Build it!
    if (Layout == "Case")
    Case();
    if (Layout == "Lid")
    Lid();
    if (Layout == "Pins") {
    color("Silver",0.5)
    PinShape();
    PinAssembly();
    }
    if (Layout == "Show") { // reveal pin assembly
    difference() {
    Case();
    translate([(CaseOffset.x – Protrusion),
    Contacts[1].y,
    Contacts[1].z])
    cube([(-CaseOffset.x + Protrusion),
    CaseSize.y,
    (CaseSize.z – Contacts[0].z + Protrusion)]);
    translate([(CaseOffset.x – Protrusion),
    (CaseOffset.y – Protrusion),
    0])
    cube([(-CaseOffset.x + Protrusion),
    Contacts[0].y + Protrusion – CaseOffset.y,
    CaseSize.z]);
    }
    color("Silver",0.15)
    PinAssembly();
    translate([0,0,Battery.z + Gap])
    Lid();
    }
    if (Layout == "Build") {
    translate([-(CaseSize.x/2 + CaseOffset.x),-(CaseOffset.y – BuildOffset),BaseThick])
    Case();
    translate([CaseSize.y/2,(CaseOffset.x/2 – BuildOffset),0])
    rotate([0,0,90])
    Lid();
    }
    if (Layout == "Fit") {
    Case();
    translate([0,0,(Battery.z + Gap)])
    Lid();
    color("Silver",0.25)
    PinAssembly();
    }
  • Makergear M2 V4 Nozzle: More Silicone!

    A Makergear forum discussion on PETG hair and the prevention thereof prompted me to take a look at the silicone coating I’d applied to the nozzle:

    M2 - nozzle silicone - applied
    M2 – nozzle silicone – applied

    That was ten months ago. This is now:

    M2 Nozzle - worn silicone coat
    M2 Nozzle – worn silicone coat

    The camera sees the nozzle in a mirror laid flat on the platform, making the image less crisp than a direct view.

    So the silicone seems a bit worn around the tip, has acquired a few firmly adhered globs, and definitely isn’t as shiny.

    Rather than (try to) peel it off and reapply a new coating, I picked off the globs, cleaned around the nozzle, and slobbered a thin layer atop the existing silicone:

    M2 Nozzle - more silicone
    M2 Nozzle – more silicone

    Extruding a few millimeters of filament pushed the film off the nozzle opening and it now works as well as it ever did.

     

  • Kindle Fire Picture Frame: Side Block

    A steel frame that Came With The House™ emerged from a hidden corner and, instants before tossing it in the recycle heap, I realized it had excellent upcycling potential:

    Kindle Fire Picture Frame - Test Run
    Kindle Fire Picture Frame – Test Run

    Stipulated: I need better pictures for not-so-techie audiences.

    Anyhow, my long-disused Kindle Fire fits perfectly into the welded-on clips, with just enough room for a right-angle USB cable, and Photo Frame Slideshow Premium does exactly what’s necessary to show pictures from internal storage with no network connection.

    All I needed was a small block holding the Kindle against the far side of the frame:

    Kindle Frame - side blocks
    Kindle Frame – side blocks

    A strip of double-stick carpet tape holds the block onto the frame. To extract the Kindle, should the need arise, slide it upward to clear the bottom clips, rotate it rearward, and out it comes.

    Getting a good block required three tries, because the basement has cooled off enough to trigger Marlin’s Thermal Runaway protection for the M2’s platform heater. A test fit after the first failure showed the long leg was 1 mm too wide and, after the second failure, I reduced the fan threshold to 15 s and the minimum layer time to 5 s, producing the third block without incident.

    The platform heater runs at 40 V and I considered bumping it to 43 V for a 15% power boost, but it has no trouble keeping up when the fan isn’t blowing chilly basement air across its surface.

    The OpenSCAD source code, such as it is, doesn’t deserve its own GitHub Gist:

    // Block to hold Kindle in a picture frame mount
    // Ed Nisley - KE4ZNU
    // November 2018
    
    Protrusion = 0.1;
    
    difference() {
    
      cube([18,44,10]);
      translate([-Protrusion,-Protrusion,-Protrusion])
        cube([18-4 + Protrusion,44-10 + Protrusion,10 + 2*Protrusion]);
    
    }
    
  • Kenmore Progressive Vacuum Tool Adapters: Third Failure

    The adapter for an old Electrolux crevice tool (not the dust brush) snapped at the usual stress concentration after about three years:

    Crevice tool adapter - broken vs PVC pipe
    Crevice tool adapter – broken vs PVC pipe

    The lower adapter is the new version, made from a length of 1 inch PVC pipe (that’s the ID, kinda-sorta) epoxied into a revised Kenmore adapter fitting.

    The original OpenSCAD model provided the taper dimensions:

    Electrolux Crevice Tool Adapter - PVC taper doodles
    Electrolux Crevice Tool Adapter – PVC taper doodles

    The taper isn’t quite as critical as it seems, because the crevice tool is an ancient molded plastic part, but a smidge over half a degree seemed like a good target.

    Start by boring out the pipe ID until it’s Big Enough (or, equally, the walls aren’t Scary Thin) at 28 mm:

    Crevice tool adapter - boring PVC
    Crevice tool adapter – boring PVC

    Alas, the mini-lathe’s craptastic compound has 2° graduations:

    Minilathe compound angle scale
    Minilathe compound angle scale

    So I set the angle using a somewhat less craptastic protractor and angle gauge:

    Crevice tool adapter - compound angle
    Crevice tool adapter – compound angle

    The little wedge of daylight near the gauge pivot is the difference between the normal perpendicular-to-the-spindle axis setting and half-a-degree-ish.

    Turning PVC produces remarkably tenacious swarf:

    Crevice tool adapter - PVC swarf
    Crevice tool adapter – PVC swarf

    The gash along the top comes from a utility knife; just pulling the swarf off didn’t work well at all.

    The column of figures down the right side of the doodles shows successive approximations to the target angle, mostly achieved by percussive adjustment, eventually converging to about the right taper with the proper dimensions.

    Cutting off the finished product with the (newly angled) cutoff bit:

    Crevice tool adapter - cutoff
    Crevice tool adapter – cutoff

    And then It Just Worked™.

    The OpenSCAD source code for all the adapters as a GitHub Gist:

    // Kenmore vacuum cleaner nozzle adapters
    // Ed Nisley KE4ZNU November 2015 and ongoing
    // Layout options
    Layout = "CrevicePipe"; // MaleFitting CoilWand FloorBrush
    // CreviceTool Crevice Pipe ScrubbyTool LuxBrush DustBrush
    //- Extrusion parameters must match reality!
    // Print with +1 shells and 3 solid layers
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    //———————-
    // Dimensions
    ID1 = 0; // for tapered tubes
    ID2 = 1;
    OD1 = 2;
    OD2 = 3;
    LENGTH = 4;
    OEMTube = [35.0,35.0,41.7,40.5,30.0]; // main fitting tube
    EndStop = [OEMTube[ID1],OEMTube[ID2],47.5,47.5,6.5]; // flange at end of main tube
    FittingOAL = OEMTube[LENGTH] + EndStop[LENGTH];
    $fn = 12*4;
    //———————-
    // 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);
    }
    //——————-
    // Male fitting on end of Kenmore tools
    // This slides into the end of the handle or wand and latches firmly in place
    module MaleFitting() {
    Latch = [40,11.5,5.0]; // rectangle latch opening
    EntryAngle = 45; // latch entry ramp
    EntrySides = 16;
    EntryHeight = 15.0; // lower edge on *inside* of fitting
    KeyRadius = 1.0;
    translate([0,0,6.5])
    difference() {
    union() {
    cylinder(d1=OEMTube[OD1],d2=OEMTube[OD2],h=OEMTube[LENGTH]); // main tube
    hull() // insertion guide
    for (i=[-(6.0/2 – KeyRadius),(6.0/2 – KeyRadius)],
    j=[-(28.0/2 – KeyRadius),(28.0/2 – KeyRadius)],
    k=[-(26.0/2 – KeyRadius),(26.0/2 – KeyRadius)])
    translate([(i – (OEMTube[ID1]/2 + OEMTube[OD1]/2)/2 + 6.0/2),j,(k + 26.0/2 – 1.0)])
    sphere(r=KeyRadius,$fn=8);
    translate([0,0,-EndStop[LENGTH]]) // wand tube butts against this
    cylinder(d=EndStop[OD1],h=EndStop[LENGTH] + Protrusion);
    }
    translate([0,0,-OEMTube[LENGTH]]) // main bore
    cylinder(d=OEMTube[ID1],h=2*OEMTube[LENGTH] + 2*Protrusion);
    translate([0,-11.5/2,23.0 – 5.0]) // latch opening
    cube(Latch);
    translate([OEMTube[ID1]/2 + EntryHeight/tan(90-EntryAngle),0,0]) // latch ramp
    translate([(Latch[1]/cos(180/EntrySides))*cos(EntryAngle)/2,0,(Latch[1]/cos(180/EntrySides))*sin(EntryAngle)/2])
    rotate([0,-EntryAngle,0])
    intersection() {
    rotate(180/EntrySides)
    PolyCyl(Latch[1],Latch[0],EntrySides);
    translate([-(2*Latch[0])/2,0,-Protrusion])
    cube(2*Latch[0],center=true);
    }
    }
    }
    //——————-
    // Refrigerator evaporator coil wand
    module CoilWand() {
    union() {
    translate([0,0,50.0])
    rotate([180,0,0])
    difference() {
    cylinder(d1=EndStop[OD1],d2=42.0,h=50.0);
    translate([0,0,-Protrusion])
    cylinder(d1=35.0,d2=35.8,h=100);
    }
    translate([0,0,50.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Samsung floor brush
    module FloorBrush() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=32.4,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=32.4,d2=30.7,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=28.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Crevice tool
    module CreviceTool() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=32.0,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=32.0,d2=30.4,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=28.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Crevice tool
    // Hacked for 1 inch Schedule 40 PVC pipe stiffening tube
    module CrevicePipe() {
    PipeOD = 33.5;
    union() {
    translate([0,0,10.0])
    rotate([180,0,0])
    difference() {
    cylinder(d1=EndStop[OD1],d2=PipeOD+2*8*ThreadWidth,h=10.0);
    translate([0,0,-Protrusion])
    cylinder(d=PipeOD,h=100);
    }
    translate([0,0,10.0])
    MaleFitting();
    }
    }
    //——————-
    // Mystery brush
    module ScrubbyTool() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=31.8,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=31.8,d2=31.0,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=26.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // eBay horsehair dusting brush
    // Hacked for 3/4" Schedule 40 PVC stiffening tube
    // eBay: 30.0 32.0 30.0
    // Shopvac: 30.3 31.0 25.0
    // Must build snout down with brim to avoid support
    module DustBrush() {
    PipeOD = 27.0; // stiffening pipe
    Snout = [0,0, 31.0, 30.3, 25.0];
    TaperLength = 10.0; // transition cone from fitting to snout
    union() {
    translate([0,0,Snout[LENGTH] + TaperLength])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=Snout[OD1],h=TaperLength);
    translate([0,0,TaperLength – Protrusion])
    cylinder(d1=Snout[OD1],d2=Snout[OD2],h=Snout[LENGTH] + Protrusion);
    }
    translate([0,0,-Protrusion]) // 3/4 inch Sch 40 PVC
    PolyCyl(PipeOD,100);
    }
    translate([0,0,Snout[LENGTH] + TaperLength – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Electrolux brush ball
    module LuxBrush() {
    union() {
    translate([0,0,30.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=30.8,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=30.8,d2=30.0,h=20.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=25.0,d2=23.0,h=30 + 2*Protrusion);
    }
    translate([0,0,30.0 – Protrusion])
    MaleFitting();
    }
    }
    //———————-
    // Build it!
    if (Layout == "MaleFitting")
    MaleFitting();
    if (Layout == "CoilWand")
    CoilWand();
    if (Layout == "FloorBrush")
    FloorBrush();
    if (Layout == "CreviceTool")
    CreviceTool();
    if (Layout == "CrevicePipe")
    CrevicePipe();
    if (Layout == "DustBrush")
    DustBrush();
    if (Layout == "ScrubbyTool")
    ScrubbyTool();
    if (Layout == "LuxBrush")
    LuxBrush();
  • MPCNC: Drag Knife Holder

    My attempt to use a HP 7475A plotter as a vinyl cutter failed due to its 19 g pen load limit:

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

    The MPCNC, however, can apply plenty of downforce, so I tinkered up a quick-and-dirty adapter to put the drag knife “pen” body into the MPCNC’s standard DW660 router holder:

    MPCNC - DW660 adapter drag knife holder - fixed position
    MPCNC – DW660 adapter drag knife holder – fixed position

    That’s using the DW660 adapter upside-down to get the business end of the knife closer to the platform. The solid model descends from the linear-bearing Sakura pen holder by ruthless pruning.

    It didn’t work well at all, because you really need a spring for some vertical compliance and control over the downforce pressure.

    Back to the Comfy Chair:

    Drag Knife Holder - DW660 Mount - solid model
    Drag Knife Holder – DW660 Mount – solid model

    A trio of the lightest springs from a 200 piece assortment (in the front left compartment) pushes the upper plate downward against the drag knife’s flange:

    MPCNC - DW660 adapter drag knife holder - spring loaded
    MPCNC – DW660 adapter drag knife holder – spring loaded

    There’s a bit more going on than may be obvious at first glance.

    The screws slide in brass tubing press-fit into the upper plate, because otherwise their threads hang up on the usual 3D printed layers inside the (drilled-out) holes. Smaller free-floating brass tubing snippets inside the springs keep them away from the screw threads; the gap between the top of the tubing and the screw head limits the vertical compliance to 3 mm. The screws thread into brass inserts epoxied into the bottom disk, with a dab of low-strength Loctite for stay-put adjustment.

    I bored the orange PETG disk to a nice slip fit around the knife body:

    DW660 drag knife holder - boring body
    DW660 drag knife holder – boring body

    The upper plate also required fitting:

    DW660 drag knife holder - boring plate
    DW660 drag knife holder – boring plate

    A few iterations produced reasonably smooth motion over a few millimeters, but it’s definitely not a low-friction / low-stiction drag knife holder. It ought to be good for some proof-of-concept vinyl cutting, though.

    The OpenSCAD source code as a GitHub Gist:

    // Drag Knife Holder for DW660 Mount
    // Ed Nisley KE4ZNU – 2018-09-26
    Layout = "Show"; // Build, Show, Puck, Mount, Plate
    /* [Extrusion] */
    ThreadThick = 0.25; // [0.20, 0.25]
    ThreadWidth = 0.40; // [0.40]
    /* [Hidden] */
    Protrusion = 0.1; // [0.01, 0.1]
    HoleWindage = 0.2;
    inch = 25.4;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    ID = 0;
    OD = 1;
    LENGTH = 2;
    //- Adjust hole diameter to make the size come out right
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    //- Dimensions
    KnifeBody = [12.0,16.0,2.0]; // body flange — resembles HP plotter pen
    WallThick = 3.0; // minimum thickness / width
    Screw = [4.0,8.5,8.0]; // holding it all together, OD = washer
    Insert = [4.0,6.0,10.0]; // brass insert
    Plate = [KnifeBody[ID],KnifeBody[OD] + 3*Screw[OD],4.0]; // spring reaction plate
    PlateGuide = [4.0,4.8,Plate[LENGTH]]; // … guide tubes
    NumScrews = 3;
    ScrewBCD = 2*(KnifeBody[OD]/2 + Screw[OD]/2 + 0.5);
    NumSides = 9*4; // cylinder facets (multiple of 3 for lathe trimming)
    // Basic shape of DW660 snout fitting into the holder
    // Lip goes upward to lock into MPCNC mount
    Snout = [44.6,50.0,9.6]; // LENGTH = ID height
    Lip = 4.0; // height of lip at end of snout
    PuckOAL = Snout[LENGTH] + Lip; // total height
    Key = [Snout[ID],25.7,PuckOAL]; // rectangular key
    module DW660Puck() {
    translate([0,0,PuckOAL])
    rotate([180,0,0]) {
    cylinder(d=Snout[OD],h=Lip/2,$fn=NumSides);
    translate([0,0,Lip/2])
    cylinder(d1=Snout[OD],d2=Snout[ID],h=Lip/2,$fn=NumSides);
    cylinder(d=Snout[ID],h=PuckOAL,$fn=NumSides);
    intersection() {
    translate([0,0,0*Lip + Key.z/2])
    cube(Key,center=true);
    cylinder(d=Snout[OD],h=Lip + Key.z,$fn=NumSides);
    }
    }
    }
    module MountBase() {
    difference() {
    DW660Puck();
    translate([0,0,-Protrusion]) // knife holder body
    PolyCyl(KnifeBody[ID],2*PuckOAL,NumSides);
    translate([0,0,PuckOAL – KnifeBody[LENGTH]/2]) // … half of flange, loose fit
    PolyCyl(KnifeBody[OD] + 2*HoleWindage,KnifeBody[LENGTH],NumSides);
    for (i=[0:NumScrews – 1]) // clamp screws
    rotate(i*360/NumScrews)
    translate([ScrewBCD/2,0,-Protrusion])
    rotate(180/8)
    PolyCyl(Insert[OD],2*PuckOAL,8);
    }
    }
    module SpringPlate() {
    difference() {
    cylinder(d=Plate[OD],h=Plate[LENGTH],$fn=NumSides);
    translate([0,0,-Protrusion]) // knife holder body
    PolyCyl(KnifeBody[ID],2*PuckOAL,NumSides);
    translate([0,0,Plate[LENGTH] – KnifeBody[LENGTH]/2]) // … half of flange, snug fit
    PolyCyl(KnifeBody[OD],KnifeBody[LENGTH],NumSides);
    for (i=[0:NumScrews – 1]) // clamp screws
    rotate(i*360/NumScrews)
    translate([ScrewBCD/2,0,-Protrusion])
    rotate(180/8)
    PolyCyl(PlateGuide[OD],2*PuckOAL,8);
    }
    }
    //—–
    // Build it
    if (Layout == "Puck")
    DW660Puck();
    if (Layout == "Plate")
    SpringPlate();
    if (Layout == "Mount")
    MountBase();
    if (Layout == "Show") {
    MountBase();
    translate([0,0,2*PuckOAL])
    rotate([180,0,0])
    SpringPlate();
    }
    if (Layout == "Build") {
    translate([0,Snout[OD]/2,0])
    MountBase();
    translate([0,-Snout[OD]/2,0])
    SpringPlate();
    }

     

  • Fireball Island Figures

    A cousin asked if my 3D printer could replace some figures gone missing from their old Fireball Island game board, a classic apparently coming out in a new & improved version.

    Fortunately, solid models exist on Thingiverse:

    Fireball Island figure - Thingiverse 536867
    Fireball Island figure – Thingiverse 536867

    Unfortunately, the left arm requires support, which Slic3r supplies with great exuberance:

    Fireball Island figure - Slic3r support
    Fireball Island figure – Slic3r support

    The vast tower on the figure’s right side (our left) seemed completely unnecessary, not to mention I have no enthusiasm for the peril inherent in chopping away so much plastic, so I replaced it with a simple in-model pillar:

    Figure Support Mods
    Figure Support Mods

    The pillar leans from an adhesion-enhancing lily pad and ends one layer below the left hand, with all dimensions and angles chosen on the fly to make the answer come out right.

    Works like a champ:

    Fireball Island Figures - orange - on platform
    Fireball Island Figures – orange – on platform

    The dark band down the middle comes from the Pixel’s shutter.

    They emerged with some PETG hair, the removal of which I left as an end-user experience.

    I mailed a small box containing figures printed in my (limited!) palette of four colors, some spares Just In Case™, and a few QC rejects showing the necessity of lily pads.

    Game on!

    The OpenSCAD source code as a GitHub Gist:

    // Adding support under Fireball Island figure arm
    import("/mnt/bulkdata/Project Files/Thing-O-Matic/Fireball Island/Fireball Island figure – 100k.stl", convexity=5);
    translate([6.5,-4.0,0]) {
    intersection(){
    translate([-10/2,-10/2,0])
    cube([10,10,11.6],center=false);
    rotate([0,-5.0,0])
    rotate(180/6)
    cylinder(d=4.0,h=30,$fn=6,center=true);
    }
    translate([8/4,0,0])
    rotate(180/6)
    cylinder(d=8,h=0.2,$fn=6);
    }