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

Making the world a better place, one piece at a time

  • MPCNC: Makerbot-style Endstop Switch Modification

    The Protoneer CNC Shield has headers for two endstops on each axis, although they’re wired to the same Arduino input pin. I installed a pair of Makerbot-style endstops on the Y axis, plugged them in, triggered one, and … the Arduino crashed. Hard. As in, a complete loss of power and reboot.

    Some fiddling around produced absolutely baffling symptoms, as I replaced each endstop board and their cables to no avail.

    Perusing the schematic (the “full instructions” link is dead, of course) eventually revealed the problem:

    Makerbot style endstop - schematic
    Makerbot style endstop – schematic

    Got it?

    Although there’s a pullup on the COM switch terminal, the switch’s NC terminal is connected to the +5 V supply, shorting across the both resistor and the LED+resistor. With two endstops in parallel, triggering one crowbars the other’s power supply to ground. I’m sure it made sense at the time, perhaps by ensuring no possible noise source could interfere with the pullup.

    The solution is simple: disconnect the NC terminal from the power supply. As it turns out, the PCB layout routes +5 V on the bottom layer, up through the via around the NC switch terminal, thence to the LED and resistor, leaving only one choice:

    MPCNC - dual MG endstop hack
    MPCNC – dual MG endstop hack

    Yup, amputate the NC terminal and be done with it.

    After that, the pullup resistor lets the endstops cooperate like you’d expect: triggering either one lights up both LEDs.

  • Sandisk 32 GB High Endurance Video Monitoring Card

    Despite my misgivings, “Ships from and sold by Amazon.com” suggests I could return a Sandisk 32 GB High Endurance MicroSD card if things turned out badly:

    MicroSD 32 GB - Samsung EVO and SanDisk High Endurance
    MicroSD 32 GB – Samsung EVO and SanDisk High Endurance

    Unlike the Samsung cards, Sandisk charges a substantial premium for not buying through Amazon.

    Verifying the card using f3probe produced the same results as with the earlier 64 GB card and copying the existing files from the Fly6 card (on the left) went smoothly:

    
    rsync -rtv /mnt/Fly6/ /mnt/part
    
    

    “High Endurance” means it’s rated for 5000 hours of “Full HD” recording, which they think occurs at 26 Mb/s. The Fly6 records video in 10 minutes chunks, each weighing about 500 MB, call it 1 MB/s = 8 Mb/s, a third of their nominal pace. One might reasonably expect this card to outlive the camera.

    As with the AS30V, we shall see …

  • MPCNC: Endstop Mount, Now With Recess

    There being nothing like a new problem to take your mind off all your old problems, now there’s a cable tie latch recess:

    X min endstop - recessed cable tie latch
    X min endstop – recessed cable tie latch

    A sectioned view of the model shows the layout:

    MPCNC MB Endstop Mount - latch recess
    MPCNC MB Endstop Mount – latch recess

    On the other side, a ramp helps bend the tie toward the MPCNC rail:

    X min endstop - recessed strap
    X min endstop – recessed strap

    Which looks thusly in the realm of applied mathematics:

    MPCNC MB Endstop Mount - strap recess
    MPCNC MB Endstop Mount – strap recess

    I’ll leave the OpenSCAD code to your imagination, because the endstop block turns out to be a bit small for the recesses. Eventually, they need a dust cover and some cleanup.

    So, there!

  • Raspberry Pi Swap File Size

    As part of some protracted flailing around while trying to get GNU Radio running on a Raspberry Pi 3, I discovered Raspbian defaults to a 100 MB swap file, rather than a swap partition, and everything I thought I knew about swap management seems inoperative. The key hint came from some notes on gr-gsm installation.

    Tweak the /etc/dphys-swapfile config file to set CONF_SWAPFACTOR=2 for a 2 GB swap file = twice the size of the Pi’s 1 GB memory.

    Start it up:

    sudo dphys-swapfile swapoff
    sudo dphys-swapfile setup
    sudo dphys-swapfile swapon
    

    And verify it worked:

    cat /proc/meminfo 
    MemTotal:         949580 kB
    MemFree:          194560 kB
    MemAvailable:     594460 kB
    Buffers:           85684 kB
    Cached:           377276 kB
    SwapCached:            0 kB
    Active:           600332 kB
    Inactive:         104668 kB
    Active(anon):     250408 kB
    Inactive(anon):    20688 kB
    Active(file):     349924 kB
    Inactive(file):    83980 kB
    Unevictable:           0 kB
    Mlocked:               0 kB
    SwapTotal:       1918972 kB
    SwapFree:        1918972 kB
    Dirty:                40 kB
    Writeback:             0 kB
    AnonPages:        242072 kB
    Mapped:           136072 kB
    Shmem:             29060 kB
    Slab:              33992 kB
    SReclaimable:      22104 kB
    SUnreclaim:        11888 kB
    KernelStack:        1728 kB
    PageTables:         3488 kB
    NFS_Unstable:          0 kB
    Bounce:                0 kB
    WritebackTmp:          0 kB
    CommitLimit:     2393760 kB
    Committed_AS:     947048 kB
    VmallocTotal:    1114112 kB
    VmallocUsed:           0 kB
    VmallocChunk:          0 kB
    CmaTotal:           8192 kB
    CmaFree:            6796 kB
    

    Then it became possible to continue flailing …

  • Samsung EVO Plus 32 GB MicroSD Cards: Verification

    A pair of known-good MicroSD cards arrived direct from Samsung at a surprisingly slight premium over the junk available on Amazon:

    Samsung EVO Plus MicroSD - 32 GB
    Samsung EVO Plus MicroSD – 32 GB

    f3probe reported they’re OK, which is no surprise:

    sudo f3probe --time-ops /dev/sdc
    F3 probe 6.0
    Copyright (C) 2010 Digirati Internet LTDA.
    This is free software; see the source for copying conditions.
    
    WARNING: Probing normally takes from a few seconds to 15 minutes, but
             it can take longer. Please be patient.
    
    Probe finished, recovering blocks... Done
    
    Good news: The device `/dev/sdc' is the real thing
    
    Device geometry:
    	         *Usable* size: 29.81 GB (62521344 blocks)
    	        Announced size: 29.81 GB (62521344 blocks)
    	                Module: 32.00 GB (2^35 Bytes)
    	Approximate cache size: 0.00 Byte (0 blocks), need-reset=no
    	   Physical block size: 512.00 Byte (2^9 Bytes)
    
    Probe time: 2'04"
     Operation: total time / count = avg time
          Read: 51.79s / 4197134 = 12us
         Write: 1'10" / 4192321 = 16us
         Reset: 1.41s / 1 = 1.41s
    

    These will go into Raspberry Pi projects, where their huge capacity won’t produce any benefit. It seems one can’t get known-good, small cards these days.

    I don’t see much point in buying known-crap counterfeits on Amazon, given their commingled-storage problem as pointed out by their helpful FBA advice:

    Use the manufacturer barcode to track inventory

    By default, your seller account is set to use the manufacturer barcode to track your eligible inventory throughout the Amazon fulfillment process. You can change this default barcode preference at any time. You have the option to change your barcode preference for each offer you create. You can also change your barcode preference for a product when you change a listing from Fulfilled by Merchant to Fulfilled by Amazon.

    Important: Items in your inventory that are identified and tracked using manufacturer barcodes are commingled with items of the same products from other sellers who also use manufacturer barcodes for those items.

    If you choose to use manufacturer barcodes, when customers purchase a product from you, Amazon can send the item that is closest to them, even if you didn’t send it to the fulfillment center. When that happens, you get the credit for the sale, and we transfer an item from your inventory to the seller whose inventory was used to fulfill the order. In addition, if you use the manufacturer barcode, you don’t have to apply an Amazon barcode to each item yourself.

    Even though inventory tracked using the manufacturer barcode is commingled within the network, the source of the inventory is tracked by our fulfillment systems and is taken into consideration if inventory problems arise.

  • Makerbot-style Endstop Power Adapter for Protoneer Arduino CNC Shield

    The Protoneer Arduino CNC shield (*) has a row of 2-pin headers for bare endstop switches. Being a big fan of LED Blinkiness, I have a stock of 3-pin Makerbot-style mechanical endstops that require a +5 V connection in addition to ground and the output.

    A crude-but-effective adapter consists of half a dozen header pins soldered to a length of stout copper wire, with a pigtail to a +5 V pin elsewhere on the board:

    3-pin to 2-pin Endstop Power Adapter
    3-pin to 2-pin Endstop Power Adapter

    A closer look:

    3-pin to 2-pin Endstop Power Adapter - detail
    3-pin to 2-pin Endstop Power Adapter – detail

    The pins get trimmed on the other side of the bus wire, because they don’t go through the PCB.

    Installed on the board, it doesn’t look like much:

    3-pin endstop adapter on Prontoneer board
    3-pin endstop adapter on Prontoneer board

    Looks like it needs either Kapton tape or epoxy, doesn’t it?

    Three more endstops at the far end of the MPCNC rails (for hard limits) will fill the unused header pins.

    (*) It’s significantly more expensive than the Chinese knockoffs, but in this case I cheerfully pay to support the guy: good stuff, direct from the source.

  • Prototype Board Holder: Now With Mounting Holes and Common Board Sizes

    The folks I’ve been coaching through their plotter build project showed it off at the local MiniMakerFaire this past weekend. Next time around, I’ll insist they secure their circuit boards and use good wiring techniques, so as to avoid destroying more stepper drivers.

    To that end, adding mounting holes to my proto board holder seems in order:

    Proto Board Holder 90x70 - Flange mounting holes - Slic3r preview
    Proto Board Holder 90×70 – Flange mounting holes – Slic3r preview

    The board dimensions now live in an associative array, so you just pick the board name from a Configurator drop-down list:

    /* [Options] */
    
    PCBSelect = "ArdUno"; // ["20x80","40x60","30x70","50x70","70x90","80x120","ArdDuemil","ArdMega","ArdPro","ArdUno","ProtoneerCNC"]
    
    PCB_NAME = 0;
    PCB_DIMENSION = 1;
    
    PCBSizes = [
      ["40x60",[40,60,1.6]],
      ["30x70",[30,70,1.6]],
      ["50x70",[50,70,1.6]],
      ["20x80",[20,80,1.6]],
      ["70x90",[70,90,1.6]],
      ["80x120",[80,120,1.6]],
      ["ArdDuemil",[69,84,1.6]],
      ["ArdMega",[102,53.5,1.6]],
      ["ArdPro",[53,53.5,1.6]],
      ["ArdUno",[69,53.1,1.6]],
      ["ProtoneerCNC",[69,53.1,1.6]],
    ];
    

    Which seems easier than keeping track of the dimensions in comments.

    You can now put the PCB clamp screws and mounting holes on specific corners & sides, allowing oddball locations for Arduino boards with corner cutouts along the right edge:

    Proto Board Holder ArdUno - Slic3r preview
    Proto Board Holder ArdUno – Slic3r preview

    A “selector” notation separates the hole location from the board dimensions & coordinates:

    ScrewSites = [
    //  [-1,1],[1,1],[1,-1],[-1,-1],        // corners
    //  [-1,0],[1,0],[0,1],[0,-1]           // middles
      [-1,1],[-1,-1],[1,0]                  // Arduinos
    ];
    

    Might not be most obvious way, but it works for me. Most of the time, corner clamps seem just fine, so I’m not sure adding the clamp and mounting hole locations to the dimension array makes sense.

    The OpenSCAD source code as a GitHub Gist:

    // Test support frame for proto boards
    // Ed Nisley KE4ZNU – Jan 2017
    // June 2017 – Add side-mount bracket, inserts into bottom
    // 2017-11 – Selectable board sizes, chassis mounting holes
    /* [Options] */
    PCBSelect = "ArdUno"; // ["20×80","40×60","30×70","50×70","70×90","80×120","ArdDuemil","ArdMega","ArdPro","ArdUno","ProtoneerCNC"]
    Layout = "Frame"; // [Frame, Bracket]
    ClampFlange = true; // external flange
    Mounts = true; // frame to chassis screw holes
    Channel = false; // wiring channel cutout
    WasherRecess = false; // cutout around screw head
    /* [Extrusion parameters] */
    ThreadThick = 0.25; // [0.15, 0.20, 0.25]
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    /* [Hidden] */
    Protrusion = 0.1;
    HoleWindage = 0.2;
    inch = 25.4;
    Tap4_40 = 0.089 * inch;
    Clear4_40 = 0.110 * inch;
    Head4_40 = 0.211 * inch;
    Head4_40Thick = 0.065 * inch;
    Nut4_40Dia = 0.228 * inch;
    Nut4_40Thick = 0.086 * inch;
    Washer4_40OD = 0.270 * inch;
    Washer4_40ID = 0.123 * inch;
    Tap6_32 = 0.106 * inch;
    Clear6_32 = 0.166 * inch;
    Head6_32 = 0.251 * inch;
    Head6_32Thick = 0.097 * inch;
    Nut6_32Dia = 0.312 * inch;
    Nut6_32Thick = 0.109 * inch;
    Washer6_32OD = 0.361 * inch;
    Washer6_32ID = 0.156 * inch;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    //- PCB sizes
    // the list must contain all the selection names as above
    //* [Hidden] */
    PCB_NAME = 0;
    PCB_DIMENSION = 1;
    PCBSizes = [
    ["40×60",[40,60,1.6]],
    ["30×70",[30,70,1.6]],
    ["50×70",[50,70,1.6]],
    ["20×80",[20,80,1.6]],
    ["70×90",[70,90,1.6]],
    ["80×120",[80,120,1.6]],
    ["ArdDuemil",[69,84,1.6]],
    ["ArdMega",[102,53.5,1.6]],
    ["ArdPro",[53,53.5,1.6]],
    ["ArdUno",[69,53.1,1.6]],
    ["ProtoneerCNC",[69,53.1,1.6]],
    ];
    PCBIndex = search([PCBSelect],PCBSizes)[0];
    PCBSize = PCBSizes[PCBIndex][PCB_DIMENSION];
    //echo(str("PCB Size Table: ",PCBSizes));
    //echo(str("PCB Select: ",PCBSelect));
    //echo(str("PCB Index: ",PCBIndex));
    echo(str("PCB Size: ",PCBSize));
    /* [Sizes] */
    WallThick = 4.0; // basic frame structure
    FrameHeight = 10.0;
    /* [Hidden] */
    Insert = [3.9,4.6,5.8];
    PCBShelf = 1.0; // width of support rim under PCB
    Clearance = 1*[ThreadWidth,ThreadWidth,0]; // around PCB on all sides
    ScrewOffset = ThreadWidth + Insert[OD]/2; // beyond PCB edges
    echo(str("Screw offset: ",ScrewOffset));
    /* [Screw Selectors] */
    // ij selectors for PCB clamp screw holes: -1/0/1 = left/center/right , bottom/center/top
    ScrewSites = [
    // [-1,1],[1,1],[1,-1],[-1,-1], // corners
    // [-1,0],[1,0],[0,1],[0,-1] // middles
    [-1,1],[-1,-1],[1,0] // Arduinos
    ];
    // ij selectors for frame mounting holes
    MountSites = [
    [0,-1],[0,1],
    // [-1,0],[1,0]
    ];
    function ScrewAngle(ij) = (ij[0]*ij[1]) ? ij[0]*ij[1]*15 : ((!ij[1]) ? 30 : 0); // align screw sides
    OAHeight = FrameHeight + Clearance[2] + PCBSize[2]; // total frame height
    echo(str("OAH: ",OAHeight));
    BossOD = 2*Washer4_40OD; // make bosses oversized for washers
    FlangeExtension = 4.0 + Washer6_32OD/2 – WallThick; // beyond frame structure
    FlangeThick = IntegerMultiple(2.0,ThreadThick); // plate under frame
    Flange = PCBSize
    + 2*[ScrewOffset,ScrewOffset,0]
    + [BossOD,BossOD,0]
    + [2*FlangeExtension,2*FlangeExtension,(FlangeThick – PCBSize[2])]
    ;
    FlangeRadius = BossOD/4;
    echo(str("Flange: ",Flange));
    NumSides = 4*5;
    WireChannel = [Flange[0],15.0,3.0 + PCBSize[2]]; // ad-hoc wiring cutout
    WireChannelOffset = [
    Flange[0]/2,0,(FrameHeight + PCBSize[2] – WireChannel[2]/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);
    FiiDia = Dia / cos(180/Sides);
    cylinder(r=(FiiDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    //- Build things
    if (Layout == "Frame")
    difference() {
    union() { // body block
    translate([0,0,OAHeight/2])
    cube(PCBSize + Clearance + [2*WallThick,2*WallThick,FrameHeight],center=true);
    for (ij = ScrewSites) // screw bosses
    if (ij[0] != 0 || ij[1] != 0)
    translate([ij[0]*(PCBSize[0]/2 + ScrewOffset),
    ij[1]*(PCBSize[1]/2 + ScrewOffset),
    0])
    cylinder(d=BossOD,h=OAHeight,$fn=NumSides);
    if (ClampFlange) // flange for work holder & mounting screw holes
    linear_extrude(height=Flange[2])
    hull()
    for (i=[-1,1], j=[-1,1]) {
    translate([i*(Flange[0]/2 – FlangeRadius),j*(Flange[1]/2 – FlangeRadius)])
    circle(r=FlangeRadius,$fn=NumSides); // convenient rounding size
    }
    }
    for (ij = ScrewSites) { // screw position indeies
    if (ij[0] != 0 || ij[1] != 0) {
    translate([ij[0]*(PCBSize[0]/2 + ScrewOffset),
    ij[1]*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(ScrewAngle(ij))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6); // screw clearance holes
    translate([ij[0]*(PCBSize[0]/2 + ScrewOffset),
    ij[1]*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(ScrewAngle(ij))
    PolyCyl(Insert[OD],OAHeight – PCBSize[2] – 3*ThreadThick + Protrusion,6); // inserts
    if (WasherRecess)
    translate([ij[0]*(PCBSize[0]/2 + ScrewOffset),
    ij[1]*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2]])
    PolyCyl(1.2*Washer4_40OD,(PCBSize[2] + Protrusion),NumSides); // optional washer recess
    }
    }
    if (Mounts)
    for (ij = MountSites)
    translate([ij[0]*(Flange[0]/2 – Washer6_32OD/2),ij[1]*(Flange[1]/2 – Washer6_32OD/2),-Protrusion])
    rotate(ScrewAngle(ij))
    PolyCyl(Clear6_32,(Flange[2] + 2*Protrusion),6);
    translate([0,0,OAHeight/2]) // through hole below PCB
    cube(PCBSize – 2*[PCBShelf,PCBShelf,0] + [0,0,2*OAHeight],center=true);
    translate([0,0,(OAHeight – (PCBSize[2] + Clearance[2])/2 + Protrusion/2)]) // PCB pocket on top
    cube(PCBSize + Clearance + [0,0,Protrusion],center=true);
    if (Channel)
    translate(WireChannelOffset) // opening for wires from bottom side
    cube(WireChannel + [0,0,Protrusion],center=true);
    }
    // Add-on bracket to hold smaller PCB upright at edge
    PCB2Insert = [3.0,4.9,4.1];
    PCB2OC = 45.0;
    if (Layout == "Bracket")
    difference() {
    hull() // frame body block
    for (i=[-1,1]) // bosses around screws
    translate([i*(PCBSize[0]/2 + ScrewOffset),0,0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    for (i=[-1,1]) // frame screw holes
    translate([i*(PCBSize[0]/2 + ScrewOffset),0,-Protrusion])
    rotate(i*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6);
    for (i=[-1,1]) // PCB insert holes
    translate([i*PCB2OC/2,(Washer4_40OD + Protrusion),OAHeight/2])
    rotate([90,0,0])
    cylinder(d=PCB2Insert[OD],h=2*(Washer4_40OD + Protrusion),$fn=6);
    }