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

  • CNC 3018-Pro: Milling the CD Fixture

    It turns out that the outer diameter of CD platters isn’t quite as perfectly controlled as you (well, I) might imagine, although the differences between CDs from different sources amounts to perhaps ±0.1 mm. Of course, instantly after putting the tape-down fixture into use, the next few discs atop my stack of scrap CDs were just large enough to not quite fit.

    The Sherline’s workspace can’t maneuver the holder’s perimeter around the spindle, so embiggening the OD calls for the rotary table. The general idea is to clamp the center of the fixture to the rotary table, run a small end mill about 0.1 mm into the fixture’s OD, spin the table one revolution, and be done with it.

    Of course, the rotary table’s 3/8-16 threaded center hole doesn’t match the fixture’s 6 mm center hole: we need an adapter. Start with a 1 inch long 3/8-16 stainless steel hex bolt, center drill the end, peel off the hex head, then turn to 6 mm OD, going down far enough so the threads don’t stick up out of the table too much:

    CNC 3018-Pro - CD fixture milling - bolt turning
    CNC 3018-Pro – CD fixture milling – bolt turning

    The Sherline uses 10-32 screws, so poke a #16 drill 15 mm into the bolt to get maybe 25% thread depth (because it’s a blind hole into stainless steel for an application requiring minimal strength and I hate breaking taps), tap 10-32, clean out the hole, and call it All Good:

    CNC 3018-Pro - CD fixture milling - rotary table adapter
    CNC 3018-Pro – CD fixture milling – rotary table adapter

    Find the trim plate from an old faucet to reach around the central boss, stack up enough flat washers to meet the nut, snug a Sherline spherical nut + washer set (because it’s within reach), chuck up a 1/8 inch mill, and have at it:

    CNC 3018-Pro - CD fixture milling
    CNC 3018-Pro – CD fixture milling

    The fixture sits atop an aluminum plate cut to fit a smaller version of the table riser, but this requires zero fancy alignment. The 6 mm adapter centers the fixture on the rotary table and the cutter sits at a fixed radius from the center wherever it contacts the fixture rim; just spin the table and it cuts a neatly centered circle.

    A test fit showed the oversize discs fit perfectly:

    CNC 3018-Pro - CD fixture milling - test fit
    CNC 3018-Pro – CD fixture milling – test fit

    Bonus: a nice new adapter for the rotary table!

  • CNC 3018-Pro: Tape-Down Platter Fixture

    Diamond drag engraving doesn’t put much sideways force on the platters, so taping the CD in place suffices to hold it:

    CNC 3018-Pro - CD taped to platform
    CNC 3018-Pro – CD taped to platform

    Wrapping a flange around the screw-down platter fixture provides plenty of surface area for tape:

    Platter Fixtures - CD on 3018 - tape flange
    Platter Fixtures – CD on 3018 – tape flange

    Which looks exactly as you think it would in real life:

    CNC 3018-Pro - CD fixture - taped
    CNC 3018-Pro – CD fixture – taped

    Admittedly, masking tape doesn’t look professional, but it’s low-profile, cheap and works perfectly. Blue painter’s tape for the “permanent” hold-down strips on the platform would be a colorful upgrade.

    It’s centered on the platform at the XY=0 origin in the middle of the XY travel limits, with edges aligned parallel to the axes. Homing the 3018 and moving to XY=0 puts the tool point directly over the center of the CD without any fussy alignment.

    The blue-and-red rings around the center hole assist probe camera alignment, whenever that’s necessary.

    The OpenSCAD source code as a GitHub Gist:

    // Machining fixtures for CD and hard drive platters
    // Ed Nisley KE4ZNU February … September 2016
    // 2019-08 split from tube base models
    PlatterName = "CD"; // [3.5inch,CD]
    CNCName = "3018"; // [3018,Sherline]
    TapeFlange = true; // Generate tape attachment
    PlateThick = 5.0; // [3.0,5.0,10.0,15.0]
    RecessDepth = 4.0; // [0.0,2.0,4.0]
    //- Extrusion parameters must match reality!
    /* [Hidden] */
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    Protrusion = 0.1; // make holes end cleanly
    inch = 25.4;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    module PolyCyl(Dia,Height,ForceSides=0) { // based on nophead's polyholes
    Sides = (ForceSides != 0) ? ForceSides : (ceil(Dia) + 2);
    FixDia = Dia / cos(180/Sides);
    cylinder(d=(FixDia + HoleWindage),h=Height,$fn=Sides);
    }
    ID = 0;
    OD = 1;
    LENGTH = 2;
    //———————-
    // Dimensions
    P_NAME = 0; // platter name
    P_ID = 1; // … inner diameter
    P_OD = 2; // … outer diameter
    P_THICK = 3; // … thickness
    PlatterData = [
    ["3.5inch", 25.0, 95.0, 1.75],
    ["CD", 15.0, 120.0, 1.20],
    ];
    PlatterSides = 3*4*5; // polygon approximation
    B_NAME = 0; // machine name
    B_OC = 1; // … platform screw OC, use small integer for slot
    B_STUD = 2; // … screw OD clearance
    BaseData = [
    ["3018", [5.0, 45.0], 6.0], // slots along X axis
    ["Sherline", [1.16*inch,1.16*inch], 5.0], // tooling plate
    ];
    PlateRound = 10.0; // corner radius
    FlangeSize = [5.0,5.0,3*ThreadThick]; // all-around tape flange
    //– calculate values based on input parameters
    PI = search([PlatterName],PlatterData,1,0)[P_NAME]; // get platter index
    echo(str("Platter: ",PlatterName));
    Platter = [PlatterData[PI][P_ID],
    PlatterData[PI][P_OD],
    PlatterData[PI][P_THICK]];
    BI = search([CNCName],BaseData,1,0)[B_NAME]; // get base index
    echo(str("Machine: ",CNCName));
    AlignOC = IntegerMultiple(Platter[OD],10);
    echo(str("Alignment pip offset: ±",AlignOC/2));
    AlignSlot = [3*ThreadWidth,10.0,3*ThreadThick];
    StudClear = BaseData[BI][B_STUD]; // … clearance
    StudOC = [IntegerMultiple(AlignOC + 2*StudClear,BaseData[BI][B_OC].x), // … screw spacing
    BaseData[BI][B_OC].y];
    echo(str("Stud spacing: ",StudOC));
    NumStuds = [2,1 + 2*floor(Platter[OD] / StudOC.y)]; // holes only along ±X edges
    echo(str("Stud holes: ",NumStuds));
    BasePlate = [(20 + StudOC.x*ceil(Platter[OD] / StudOC.x)),
    (10 + AlignOC),
    PlateThick];
    echo(str("Plate: ",BasePlate));
    Flange = [BasePlate.x + 2*FlangeSize.x,BasePlate.y + 2*FlangeSize.y,FlangeSize.z];
    echo(str("Flange: ",Flange));
    //———————-
    // Drilling fixture for disk platters
    module PlatterFixture() {
    difference() {
    union() {
    hull() // basic plate shape
    for (i=[-1,1], j=[-1,1])
    translate([i*(BasePlate.x/2 – PlateRound),j*(BasePlate.y/2 – PlateRound),0])
    cylinder(r=PlateRound,h=BasePlate.z,$fn=4*4);
    if (TapeFlange)
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(Flange.x/2 – PlateRound),
    j*(Flange.y/2 – PlateRound),
    0])
    cylinder(r=PlateRound,h=FlangeSize.z,$fn=4*4);
    }
    for (i=[-1,0,1], j=[-1,0,1]) // origin pips
    translate([i*AlignOC/2,j*AlignOC/2,BasePlate.z – 2*ThreadThick])
    cylinder(d=4*ThreadWidth,h=1,$fn=6);
    for (i=[-1,1], j=[-1,1]) { // alignment slots
    translate([i*(AlignOC + AlignSlot.x)/2,
    j*Platter[OD]/4,
    (BasePlate.z – AlignSlot.z/2 + Protrusion/2)])
    cube(AlignSlot + [0,0,Protrusion],center=true);
    translate([i*Platter[OD]/4,
    j*(AlignOC + AlignSlot.x)/2,
    (BasePlate.z – AlignSlot.z/2 + Protrusion/2)])
    rotate(90)
    cube(AlignSlot + [0,0,Protrusion],center=true);
    }
    for (i=[-1,1], j=[-floor(NumStuds.y/2):floor(NumStuds.y/2)]) // mounting stud holes
    translate([i*StudOC.x/2,j*StudOC.y/2,-Protrusion])
    rotate(180/6)
    PolyCyl(StudClear,BasePlate.z + 2*Protrusion,6);
    translate([0,0,-Protrusion]) // center clamp hole
    rotate(180/6)
    PolyCyl(StudClear,BasePlate.z + 2*Protrusion,6);
    translate([0,0,BasePlate.z – Platter[LENGTH]]) // disk locating recess
    rotate(180/PlatterSides)
    linear_extrude(height=(Platter[LENGTH] + Protrusion),convexity=2)
    difference() {
    circle(d=(Platter[OD] + 2*HoleWindage),$fn=PlatterSides);
    circle(d=Platter[ID] – HoleWindage,$fn=PlatterSides);
    }
    translate([0,0,BasePlate.z – RecessDepth]) // drilling recess
    rotate(180/PlatterSides)
    linear_extrude(height=(RecessDepth + Protrusion),convexity=2)
    difference() {
    circle(d=(Platter[OD] – 10),$fn=PlatterSides);
    circle(d=(Platter[ID] + 10),$fn=PlatterSides);
    }
    }
    }
    //———————-
    // Build it
    PlatterFixture();

  • DRV8825 Stepper Driver: Adding a Home Output

    The DRV8825 stepper driver chip has a -Home output going active during the (micro)step corresponding to 45°, where both winding currents equal 71% of the peak value:

    DRV8825 pinout
    DRV8825 pinout

    Unfortunately, pin 27 is another unconnected pin on the DRV8825 PCB, without even a hint of a pad for E-Z soldering.

    It’s also an open-drain output in need of a pullup, so I globbed on a 1/8 W 10 kΩ resistor in addition to the tiny wire from the IC pad to the left header pin:

    DRV8825 PCB - Home signal output
    DRV8825 PCB – Home signal output

    Read it from the right: brown black black red gold. Even in person, the colors don’t look like that, not even a little bit: always measure before installation!

    The right header pin is firmly soldered to the PCB ground pin I also used for the 1:8 microstep hack. The whole affair received a generous layer of hot melt glue in the hope of some mechanical stabilization, although hanging a scope probe off those pins can’t possibly end well.

    The general idea is to provide a scope sync output independent of the motor speed, so I can look at the current waveforms:

    3018 X - Fast - 12V - 140mm-min 1A-div
    3018 X – Fast – 12V – 140mm-min 1A-div

    The alert reader will note the pulse occurs on the down-going side of the waveforms, which means I have the current probes clipped on backwards or, equivalently, on the wrong wire. The point is to get a stable sync, so it’s all good no matter which way the current goes.

  • CNC 3018-Pro: LM6UU Linear-bearing Diamond Drag Bit Holder

    The CNC 3018-Pro normally holds a small DC motor with a nicely cylindrical housing,so this was an easy adaptation of the MPCNC’s diamond drag bit holder:

    CNC 3018-Pro - Diamond bit - overview
    CNC 3018-Pro – Diamond bit – overview

    The lip around the bottom part rests atop the tool clamp, with the spring reaction plate sized to clear the notch in the Z-axis stage.

    The solid model looks about like you’d expect:

    Diamond Scribe - Mount - solid model
    Diamond Scribe – Mount – solid model

    The New Thing compared to the MPCNC holder is wrapping LM6UU bearings around an actual 6 mm shaft, instead of using LM3UU bearings for the crappy diamond bit shank:

    CNC 3018-Pro - Diamond bit - epoxy curing
    CNC 3018-Pro – Diamond bit – epoxy curing

    I cut the shank in two pieces, epoxied them into 3 mm holes drilled into the 6 mm shaft, then epoxied the knurled stop ring on the end. The ring is curing in the bench block to stay perpendicular to the 6 mm shaft.

    The spring constant is 55 g/mm and it’s now set for 125 g preload:

    CNC 3018-Pro - Diamond bit - force measurement
    CNC 3018-Pro – Diamond bit – force measurement

    A quick test says all the parts have begun flying in formation:

    CNC 3018-Pro - Diamond bit - test CD
    CNC 3018-Pro – Diamond bit – test CD

    It’s definitely more rigid than the MPCNC!

    The OpenSCAD source code as a GitHub Gist:

    // Diamond Scribe in linear bearings for CNC3018
    // Ed Nisley KE4ZNU – 2019-08-9
    Layout = "Build"; // [Build, Show, Base, Mount, Plate]
    /* [Hidden] */
    ThreadThick = 0.25; // [0.20, 0.25]
    ThreadWidth = 0.40; // [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
    // Knife holder & suchlike
    ScribeOD = 3.0; // diamond scribe shaft
    Bearing = [6.0,12.0,19.0]; // linear bearing body, ID = shaft diameter
    Spring = [4.5,5.5,3*ThreadThick]; // compression spring around shaft, LENGTH = socket depth
    //Spring = [9.5,10.0,3*ThreadThick]; // compression spring around shaft, LENGTH = socket depth
    WallThick = 4.0; // minimum thickness / width
    Screw = [3.0,6.75,25.0]; // holding it all together, OD = washer
    Insert = [3.0,5.0,8.0]; // brass insert
    //Insert = [4.0,6.0,10.0];
    Clamp = [43.2,44.0,34.0]; // tool clamp ring, OD = clearance around top
    LipHeight = IntegerMultiple(2.0,ThreadThick); // above clamp for retaining
    BottomExtension = 25.0; // below clamp to reach workpiece
    MountOAL = LipHeight + Clamp[LENGTH] + BottomExtension; // total mount length
    echo(str("Mount OAL: ",MountOAL));
    Plate = [1.5*ScribeOD,Clamp[ID] – 0*2*WallThick,WallThick]; // spring reaction plate
    NumScrews = 3;
    ScrewBCD = Bearing[OD] + Insert[OD] + 2*WallThick;
    echo(str("Retainer max OD: ",ScrewBCD – Screw[OD]));
    NumSides = 9*4; // cylinder facets (multiple of 3 for lathe trimming)
    // Basic mount shape
    module CNC3018Base() {
    translate([0,0,MountOAL – LipHeight])
    cylinder(d=Clamp[OD],h=LipHeight,$fn=NumSides);
    translate([0,0,MountOAL – LipHeight – Clamp[LENGTH] – Protrusion])
    cylinder(d=Clamp[ID],h=(Clamp[LENGTH] + 2*Protrusion),$fn=NumSides);
    cylinder(d1=Bearing[OD] + 2*WallThick,d2=Clamp[ID],h=BottomExtension + Protrusion,$fn=NumSides);
    }
    // Mount with holes & c
    module Mount() {
    difference() {
    CNC3018Base();
    translate([0,0,-Protrusion]) // bearing
    PolyCyl(Bearing[OD],2*MountOAL,NumSides);
    for (i=[0:NumScrews – 1]) // clamp screws
    rotate(i*360/NumScrews)
    translate([ScrewBCD/2,0,MountOAL – Clamp[LENGTH]])
    rotate(180/8)
    PolyCyl(Insert[OD],Clamp[LENGTH] + Protrusion,8);
    }
    }
    module SpringPlate() {
    difference() {
    cylinder(d=Plate[OD],h=Plate[LENGTH],$fn=NumSides);
    translate([0,0,-Protrusion])
    PolyCyl(Plate[ID],2*MountOAL,NumSides);
    translate([0,0,Plate[LENGTH] – Spring[LENGTH]]) // spring retainer
    PolyCyl(Spring[OD],Spring[LENGTH] + Protrusion,NumSides);
    for (i=[0:NumScrews – 1]) // clamp screws
    rotate(i*360/NumScrews)
    translate([ScrewBCD/2,0,-Protrusion])
    rotate(180/8)
    PolyCyl(Screw[ID],2*MountOAL,8);
    }
    }
    //—–
    // Build it
    if (Layout == "Base")
    CNC3018Base();
    if (Layout == "Mount")
    Mount();
    if (Layout == "Plate")
    SpringPlate();
    if (Layout == "Show") {
    Mount();
    translate([0,0,1.25*MountOAL])
    rotate([180,0,0])
    SpringPlate();
    }
    if (Layout == "Build") {
    translate([0,-0.75*Clamp[OD],MountOAL])
    rotate([180,0,0])
    Mount();
    translate([0,0.75*Plate[OD],0])
    SpringPlate();
    }

  • Floor Sweepings from eBay

    Ordered 100 stainless steel M3 washers from a “US Seller”, received this:

    M3 stainless steel washers - short count
    M3 stainless steel washers – short count

    Yeah, it looked a bit short to me, too.

    The chopped and bent washers in the upper right corner suggest the seller got floor sweepings from his source, which is about what you’d expect for a bottom-dollar vendor.

    The seller refunded half, which wasn’t particularly generous, but I wasn’t ready to go to the mat for two bucks.

  • Raspberry Pi “Moster” Heatsink Retaping

    A pair of colorful laser-cut stacked acrylic Raspberry Pi cases with “Moster” (*) heatsinks arrived, with the intent of dressing up the HP 7475A plotters for their next Show-n-Tell:

    Moster RPi Heatsink - assembled case
    Moster RPi Heatsink – assembled case

    Unfortunately, the thermal tape on one of the CPU heatsinks was sufficiently wrinkled to prevent good contact with the CPU:

    RPi taped heatsinks - as received
    RPi taped heatsinks – as received

    The seller sent a replacement copper slug with tape on one side. Presumably, they glue it to the heatsink with thermal silicone:

    Moster RPi Heatsink - silicone adhesive
    Moster RPi Heatsink – silicone adhesive

    Of which, I have none on hand.

    So I did what I should have done originally, which was to drop a few bucks on a lifetime supply of thermally conductive heatsink tape, apply it to the bare side of the slug and stick the slug to the heatsink with their tape:

    Moster RPi Heatsink - replacement adhesive tape
    Moster RPi Heatsink – replacement adhesive tape

    The blue stuff is the separation film, with the tape being white. It doesn’t match the black tape on the other side, but seems gooey enough to work.

    Done!

    Despite the heatsink hype, ball grid array chips dissipate most of their heat through their pads (and perhaps a central thermal pad) into the PCB, so sticking a heatsink atop the package is largely decorative, along the lines of hotrod ornamentation.

    The epoxy packages used in previous Raspberry Pi iterations had better thermal conductivity to their top surface:

    RPi 3 B - epoxy CPU
    RPi 3 B – epoxy CPU

    Than the more recent metal-top packages, which surely have inert-gas fill under the lid:

    RPi 3 B - metal CPU
    RPi 3 B – metal CPU

    Pix cropped after being pilfered from the Official Raspberry Pi site.

    Yes, the heatsink does conduct some heat into the air, even if not nearly as much as you might want.

    (*) I’m pretty sure “Moster” was a typo in the original eBay listing which took on a life of its own to become something of an unofficial trademark. All of the search results ship from Duluth, Georgia (USA), regardless of the nominal seller; feel free to draw your own conclusions.

  • CNC 3018-Pro: Table Riser

    With the 3018-Pro used for drag engraving on CDs and hard drive platters, there’s no need for all the clearance below the Z-axis carriage required for the OEM motor and ER11 collet chuck. A chunk of laminate countertop and a hunk of Celotex foam insulation produce a nicely flat surface 47 mm above the platform:

    CNC 3018 Table Riser
    CNC 3018 Table Riser

    It’s surprisingly flat:

    Table Flatness Measurement - 2019-08-30
    Table Flatness Measurement – 2019-08-30

    Those are millimeters of clearance between the gray plastic clamp around the diamond drag tool holder (about which, more later) and my trusty bench block, measured at 50 mm intervals across the platform. The lower figures appeared after tightening the upper-left screw by a little over 1/6 turn = 0.2 mm, making the entire platform flat & aligned within ±0.1 mm.

    Yeah, not bad for a scrap countertop!

    The four M6 socket head cap screws pass through the stack into T-nuts in the platform:

    CNC 3018 Table Riser - screw clearance
    CNC 3018 Table Riser – screw clearance

    The countertop was thick enough to allow countersinking the screws slightly below the surface:

    CNC 3018 Table Riser - screw countersink
    CNC 3018 Table Riser – screw countersink

    I transfer-punched the screw clearance hole locations into the Celotex and drilled it with an ordinary twist drill. It wasn’t pretty, but nobody will ever notice.

    Two sheets, maybe 1 mm thick, of closed-cell foam below the Celotext provide enough squish to align the top surface without straining anything. The screws are firmly tight, so they shouldn’t work their way loose under minimal engraving loads.

    Taping the CDs to the surface works well for now, although a simpler version of the fixture may be in order.