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.

The New Hotness

  • MPCNC: Z-Axis Height Probe

    A slight modification to the MPCNC LM12UU collet pen holder turns it into a long-reach Z-Axis Height Probe:

    CNC 3018-Pro - Z-Axis height probe - overview
    CNC 3018-Pro – Z-Axis height probe – overview

    A flange on the top plate holds a Makerbot-style endstop switch:

    Collet Holder - LM12UU - switch plate - solid model
    Collet Holder – LM12UU – switch plate – solid model

    The brass probe rod sports a 3/32 inch ball epoxied on its tip, although for my simple needs I could probably use the bare rod:

    CNC 3018-Pro - Z-Axis height probe - ball tip detail
    CNC 3018-Pro – Z-Axis height probe – ball tip detail

    I clamped the rod to extend a bit beyond the plate, where it can soak up most of the switch release travel, leaving just enough to reset the clickiness after each probe:

    CNC 3018-Pro - Z-Axis height probe - detail
    CNC 3018-Pro – Z-Axis height probe – detail

    The probe responds only to Z motion, not tip deflection in XY, so it’s not particularly good for soft objects with sloped sides, like the insole shown above. It works fine for rigid objects and should suffice to figure the modeling workflow.

    The bCNC Auto-Level probe routine scans a grid over a rectangular region:

    Insole - bCNC AutoLevel Probe Map - detail
    Insole – bCNC AutoLevel Probe Map – detail

    Which Meshlab turns into a solid model:

    Insole - Meshlab triangulation
    Insole – Meshlab triangulation

    That’s the bottom of the insole probed on a 5 mm grid, which takes something over an hour to accomplish.

    The OpenSCAD code as a GitHub Gist:

    // Collet pen cartridge holder using LM12UU linear bearing
    // Ed Nisley KE4ZNU – 2019-04-26
    // 2019-06 Adapted from LM12UU drag knife holder
    // 2019-09 Probe switch mount plate
    Layout = "Build"; // [Build, Show, Puck, Mount, Plate, SwitchPlate]
    /* [Hidden] */
    // Extrusion parameters
    ThreadThick = 0.25; // [0.20, 0.25]
    ThreadWidth = 0.40; // [0.40]
    // Constants
    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
    // 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
    // Holder & suchlike
    PenShaft = 3.5; // hole to pass pen cartridge
    WallThick = 4.0; // minimum thickness / width
    Screw = [4.0,8.5,25.0]; // thread ID, washer OD, length
    Insert = [4.0,6.0,10.0]; // brass insert
    Bearing = [12.0,21.0,30.0]; // linear bearing body
    Plate = [PenShaft,Snout[OD] – WallThick,WallThick]; // spring reaction plate
    echo(str("Plate: ",Plate));
    SpringSeat = [0.56,7.5,2*ThreadThick]; // wire = ID, coil = OD, seat depth = length
    PuckOAL = max(Bearing[LENGTH],(Snout[LENGTH] + Lip)); // total height of DW660 fitting
    echo(str("PuckOAL: ",PuckOAL));
    Key = [Snout[ID],25.7,(Snout[LENGTH] + Lip)]; // rectangular key
    NumScrews = 3;
    //ScrewBCD = 2.0*(Bearing[OD]/2 + Insert[OD]/2 + WallThick);
    ScrewBCD = (Snout[ID] + Bearing[OD])/2;
    echo(str("Screw BCD: ",ScrewBCD));
    NumSides = 9*4; // cylinder facets (multiple of 3 for lathe trimming)
    // MBI Endstop switch PCB
    PCB = [40.0,1.6,16.5]; // endstop PCB, switch downward, facing parts
    Touchpoint = [-4.8,4.8,4.5]; // contact point from PCB edges, solder side
    TapeThick = 1.0; // foam mounting tape
    SwitchMount = [PCB.x,WallThick,PCB.z + Touchpoint.z + Plate.z];
    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=(Snout[LENGTH] + Lip),$fn=NumSides);
    translate([0,0,(Snout[LENGTH] + Lip) – Protrusion])
    cylinder(d1=Snout[ID],d2=2*WallThick + Bearing[OD],h=PuckOAL – (Snout[LENGTH] + Lip),$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]) // bearing
    PolyCyl(Bearing[OD],2*PuckOAL,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]) // pen cartridge hole
    PolyCyl(PenShaft,2*Plate[LENGTH],NumSides);
    translate([0,0,Plate.z – SpringSeat[LENGTH]]) // spring retaining recess
    PolyCyl(SpringSeat[OD],SpringSeat[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*PuckOAL,8);
    }
    }
    module SwitchPlate() {
    translate([0,0,Plate.z])
    rotate([180,0,0])
    SpringPlate();
    rotate(45)
    translate([Touchpoint.x,Touchpoint.y + TapeThick,0])
    cube(SwitchMount,center=false);
    }
    //—–
    // Build it
    if (Layout == "Puck")
    DW660Puck();
    if (Layout == "Plate")
    SpringPlate();
    if (Layout == "SwitchPlate")
    SwitchPlate();
    if (Layout == "Mount")
    MountBase();
    if (Layout == "Show") {
    MountBase();
    translate([0,0,1.6*PuckOAL])
    rotate([180,0,0])
    SpringPlate();
    }
    if (Layout == "Build") {
    translate([0,Snout[OD]/2,PuckOAL])
    rotate([180,0,0])
    MountBase();
    translate([0,-Snout[OD]/2,0])
    SpringPlate();
    }