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

  • Cheap WS2812 LEDs: Test Fixture Mount

    Mounting the ungainly WS2812 LED test fixture seemed like a Good Idea to keep the electricity out of the usual conductive litter:

    WS2812 array test fixture - rear
    WS2812 array test fixture – rear

    The solid model shows more details:

    LED Test Fixture - solid model
    LED Test Fixture – solid model

    The power wires along the array edges slide into the rear (thinner) slot, with enough friction from a few gentle bends to hold the whole mess in place.

    The knockoff Arduino Nano rests on the recessed ledge in the pit, with M2 screws and washers at the corners holding it down (the PCB’s built-in holes might work with 1 mm or 0-90 screws, but that’s just crazy talk). I soldered the power wires directly to the coaxial jack pins under the PCB; they snake out to the LEDs through the little trench. There should be another cutout around the USB connector for in-situ programming, although the existing code works fine.

    The front (wider) slot holds a piece of translucent white acrylic to diffuse the light:

    WS2812 array test fixture - front flash
    WS2812 array test fixture – front flash

    It’s painfully bright: a few layers of neutral density filter would be appropriate for a desk toy.

    The array runs hot enough at MaxPWM = 255 to produce a gentle upward breeze.

    It looks even better without the flash:

    WS2812 array test fixture - front dark
    WS2812 array test fixture – front dark

    You’ll find many easier ways to get RGB LED panels, but that’s not the point here; I’m waiting for these things to die an unnatural death.

    The OpenSCAD source code as a GitHub Gist:

    // LED Test Fixture
    // Ed Nisley KE4ZNU – February 2017
    ClampFlange = true;
    Channel = false;
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //- Screw sizes
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Insert = [2.8,3.5,4.0]; // M2 threaded insert
    ScrewOD = 2.0;
    WasherOD = 5.0;
    //- Component sizes
    PCBSize = [18.0,43.5,1.6]; // microcontroller PCB
    PCBClear = 2*[ThreadWidth,ThreadWidth,0]; // clearance around board
    PCBShelf = [ThreadWidth,ThreadWidth,0]; // shelf under perimeter
    PCBCavity = PCBSize – PCBShelf + [0,0,2.5]; // support shelf around bottom parts
    LEDPanel = [70,40,4.0]; // lying flat, LEDs upward
    LEDWire = [LEDPanel[0],LEDPanel[1] + 2*5.0,2.0]; // power wires along sides
    Diffuser = [LEDPanel[0],LEDPanel[1] + 2*4.0,3.5];
    echo(str("Diffuser panel: ",Diffuser));
    WallThick = 8.0;
    BaseThick = 3*ThreadThick + Insert[LENGTH] + PCBCavity[2];
    Block = [3*WallThick + PCBSize[0] + LEDPanel[2] + Diffuser[2],
    2*WallThick + IntegerMultiple(max(PCBSize[1],LEDWire[1]),5),
    BaseThick + LEDPanel[0]];
    echo(str("Block: ",Block));
    CornerRadius = 5.0;
    NumSides = 4*5;
    //- 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);
    }
    //- Build it
    difference() {
    hull() // main block with rounded corners
    for (i=[-1,1], j=[-1,1])
    translate([i*(Block[0]/2 – CornerRadius),j*(Block[1]/2 – CornerRadius),,0])
    cylinder(r=CornerRadius,h=Block[2],$fn=NumSides);
    translate([2*WallThick + PCBSize[0] – Block[0],
    0,
    (Block[2]/2 + BaseThick)])
    cube(Block + [0,2*Protrusion,0],center=true); // cut out over PCB
    translate([WallThick + (PCBSize + PCBClear)[0]/2 – Block[0]/2,
    0,
    0]) {
    translate([0,0,(BaseThick + (Protrusion – PCBSize[2])/2)])
    cube(PCBSize + PCBClear + [0,0,Protrusion],center=true); // PCB recess
    translate([0,0,(BaseThick + (Protrusion – PCBCavity[2])/2)])
    cube(PCBCavity + [0,0,Protrusion],center=true); // cavity under PCB
    translate([PCBSize[0]/2 + WallThick/2 – Protrusion/2,PCBSize[1]/2 – 15/2,BaseThick – PCBCavity[2]/2 + Protrusion/2])
    cube([WallThick + PCBShelf[0] + Protrusion,
    15,PCBCavity[2] + Protrusion],center=true); // wiring cutout
    for (i=[-1,1], j=[-1,1]) // screw inserts
    translate([i*(PCBSize[0] + ScrewOD)/2,j*(PCBSize[1] + ScrewOD)/2,-Protrusion])
    rotate(180/(2*6))
    PolyCyl(Insert[OD],BaseThick + 2*Protrusion,6);
    }
    resize([2*Block[0],0,LEDPanel[0] + Protrusion]) // LED panel outline
    translate([0,0,BaseThick])
    rotate([0,-90,0])
    translate([(LEDPanel[0] + Protrusion)/2,0,0])
    cube(LEDPanel + [Protrusion,0,0],center=true);
    translate([-Block[0]/2 + 2*WallThick + PCBSize[0] + LEDWire[2]/2 + 5*ThreadWidth,
    0,BaseThick]) // LED wiring recess
    rotate([0,-90,0])
    translate([(LEDWire[0] + Protrusion)/2,0,0])
    cube(LEDWire + [Protrusion,0,0],center=true);
    translate([Block[0]/2 – Diffuser[2]/2 – 5*ThreadWidth,0,BaseThick]) // diffuser
    rotate([0,-90,0])
    translate([(Diffuser[0] + Protrusion)/2,0,0])
    cube(Diffuser + [Protrusion,0,0],center=true);
    }
  • Bandsaw Worklight: LED Cable Clips

    Adapting the sewing machine cable clips for larger USB cables:

    LED Cable Clips - solid model
    LED Cable Clips – solid model

    The calculation positioning the posts wasn’t quite right; they now touch the cable OD at their midline and converge slightly overhead to retain it.

    They’re great candidates for sequential printing:

    LED Cable Clips - Slic3r - sequential print
    LED Cable Clips – Slic3r – sequential print

    With the basement at 14 °C, any cooling is too much: the platform heater can’t keep the bed above the thermal cutout temperature, the firmware concludes the thermistor has failed, and shuts the printer off. So I popped the four finished clips off the platform, removed the skirt, unplugged the fan, rebooted that sucker, and restarted the print.

    One clip in the front keeps the cable away from the power switch and speed control directly below the gooseneck mount:

    USB Gooseneck Mount - cable clip
    USB Gooseneck Mount – cable clip

    A few clips in the back route the cable from the COB LED epoxied directly onto the bandsaw frame away from the motor enclosure:

    Bandsaw platform COB LED - cable clips
    Bandsaw platform COB LED – cable clips

    They’re mounted on double-sided foam tape. The COB LED on the frame isn’t anything to write home about, but you can see the foam tape peeking out around the clip base:

    Bandsaw platform COB LED
    Bandsaw platform COB LED

    Unlike those LED filaments, it seems you can gently bend the aluminum substrate under a COB LED.

    The bandsaw platform now has plenty of light: a fine upgrade!

    Yeah, you can buy stick-on cable anchors, but what’s the fun in that? These fit exactly, hold securely, and work just fine.

    The OpenSCAD source code as a GitHub Gist:

    // LED Cable Clips
    // Ed Nisley – KE4ZNU – October 2014
    // February 2017 – adapted for USB cables
    Layout = "Show"; // Show Build
    //- Extrusion parameters must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2; // extra clearance
    Protrusion = 0.1; // make holes end cleanly
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    //———————-
    // Dimensions
    Base = [15.0,15.0,6*ThreadThick]; // base over sticky square
    CableOD = 3.8;
    BendRadius = 5.0;
    CornerRadius = Base[0]/5;
    CornerSides = 4*4;
    NumSides = 6*3;
    //– Oval clip with central passage
    module OvalPass() {
    intersection() {
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(Base[0]/2 – CornerRadius),j*(Base[1]/2 – CornerRadius),0])
    rotate(180/CornerSides)
    cylinder(r=CornerRadius,h=Base[2] + 1.00*CableOD,$fn=CornerSides,center=false);
    union() {
    translate([0,0,Base[2]/2]) // oversize mount base
    scale([2,2,1])
    cube(Base,center=true);
    for (j=[-1,1]) // bending ovals
    translate([0,j*(Base[1]/2 – 0.125*(Base[1] – CableOD)/2),(Base[2] – Protrusion)])
    resize([Base[0]/0.75,0,0])
    cylinder(d1=0.75*(Base[1]-CableOD),
    d2=(Base[1]-CableOD)/cos(0*180/NumSides),
    h=(CableOD + Protrusion),
    center=false,$fn=NumSides);
    }
    }
    if (Layout == "Show")
    color("Red",0.3)
    translate([0,0,Base[2] + CableOD/2])
    rotate([0,90,0])
    cylinder(d=CableOD,h=2*Base[0],center=true,$fn=48);
    }
    //———————-
    // Build it
    OvalPass();
  • Bandsaw Worklight: USB Gooseneck Mount

    The bandsaw now sports a chunky mount for its gooseneck light:

    USB Gooseneck Mount - on bandsaw
    USB Gooseneck Mount – on bandsaw

    The gooseneck ends in a USB Type-A plug, so an ordinary USB extension cable can connect it to the hacked hub supplying 9 VDC:

    USB Gooseneck Mount - interior
    USB Gooseneck Mount – interior

    The plastic came from a slightly earlier version of the solid model, with one foam pad under the gooseneck’s USB plug to soak up the clearance. The four smaller holes, with M3 brass inserts visible in the bottom half (on the right), clamp the gooseneck connector in place against the foam; you could push it out if you were really determined, but you’d have to be really determined.

    If I ever build another one, it’ll sandwich the plug between opposing pads:

    USB Gooseneck Connector Mount - Slic3r preview
    USB Gooseneck Connector Mount – Slic3r preview

    The lettering on the block stands out much better in the solid model:

    USB Gooseneck Connector Mount - solid model - overview
    USB Gooseneck Connector Mount – solid model – overview

    Obviously, I need help with the stylin’ thing. This looks better, but with terrible overhangs for printing in the obvious no-support orientation:

    USB Gooseneck Connector Mount - solid model - rounded top
    USB Gooseneck Connector Mount – solid model – rounded top

    Anyhow, the USB extension cable (on the left) has plenty of clearance and pulls straight out of the housing, so I can remove the bandsaw cover without unwiring:

    USB Gooseneck Mount - assembled
    USB Gooseneck Mount – assembled

    The LED ticks along at 40 °C in a 14 °C basement, suggesting a thermal coefficient around 14 °C/W. Even in the summer months, with the basement around 25 °C, there’s no risk of PETG softening at 50 °C.

    I’ll epoxy a similar 1.8 W COB LED onto the curve of the bandsaw frame where it can shine on the left and rear part of the table; it doesn’t even need a case.

    The OpenSCAD source code as a GitHub Gist:

    // Gooseneck lamp for MicroMark bandsaw
    // Ed Nisley KE4ZNU
    // February 2017
    Layout = "Mount"; // Mount Show Build
    Gap = 5; // distance between halves for Show
    //- Extrusion parameters must match reality!
    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);
    //———————-
    // Dimensions
    Tap10_32 = 0.159 * inch;
    Clear10_32 = 0.190 * inch;
    Head10_32 = 0.373 * inch;
    Head10_32Thick = 0.110 * inch;
    Nut10_32Dia = 0.433 * inch;
    Nut10_32Thick = 0.130 * inch;
    Washer10_32OD = 0.381 * inch;
    Washer10_32ID = 0.204 * inch;
    ID = 0; // for round things
    OD = 1;
    LENGTH = 2;
    Insert = [3.0,4.9,2*ThreadThick + IntegerMultiple(4.2,ThreadThick)]; // M3 short brass insert
    CornerRadius = 5.0; // rounded mount block corners for pretty
    CornerSides = 4*4;
    RoundedTop = true; // true for fancy smooth top edges
    USBPlug = [39.0,16.0,8.3]; // plug, X from base of plug
    USBSocket = [28.0,20.0,11.5]; // USB extension, X from tip of socket
    USBMating = [-12.0,0,0]; // offset of plug base relative to block center
    Foam = [35.0,10.0,2.0 – 1.0]; // foam pad to secure USB plug (Z = thickness – compression)
    GooseneckOD = 5.0; // flexy gooseneck diameter
    MountScrewOC = 35.0; // make simple screw hole spacing for bandsaw case
    MountBlock = [10*round((USBPlug[0] + USBSocket[0] + 5.0)/10),
    10*round((MountScrewOC + Washer10_32OD + 5.0)/10),
    // 2*6*ThreadThick + IntegerMultiple(max(USBPlug[2],USBSocket[2]),ThreadThick)];
    16.0]; // thickness = 16 mm M3x0.5 button head screw
    echo(str("Block size: ",MountBlock));
    LegendDepth = 2*ThreadThick; // lettering depth
    //———————-
    // 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);
    }
    //– Mount
    module Mount() {
    difference() {
    hull()
    if (RoundedTop) {
    for (i=[-1,1], j=[-1,1])
    translate([i*(MountBlock[0]/2 – CornerRadius),j*(MountBlock[1]/2 – CornerRadius),0]) {
    translate([0,0,-MountBlock[2]/2])
    rotate(180/CornerSides)
    cylinder(r=CornerRadius,h=MountBlock[2]/2,$fn=CornerSides,center=false);
    translate([0,0,MountBlock[2]/2 – CornerRadius])
    rotate(180/CornerSides)
    sphere(r=CornerRadius,$fn=CornerSides,center=true);
    }
    }
    else {
    for (i=[-1,1], j=[-1,1])
    translate([i*(MountBlock[0]/2 – CornerRadius),j*(MountBlock[1]/2 – CornerRadius),0])
    rotate(180/CornerSides)
    cylinder(r=CornerRadius,h=MountBlock[2],$fn=CornerSides,center=true);
    }
    for (j=[-1,1]) // screws into bandsaw case
    translate([0,j*MountScrewOC/2,-(MountBlock[2]/2 + Protrusion)])
    rotate(180/8)
    PolyCyl(Clear10_32,(MountBlock[2] + 2*Protrusion),8);
    for (i=[-1,1], j=[-1,1]) { // clamp screws
    translate([i*MountBlock[0]/4,j*MountScrewOC/2,-MountBlock[2]])
    PolyCyl(Insert[ID],2*MountBlock[2],6); // clearance
    translate([i*MountBlock[0]/4,j*MountScrewOC/2,-(MountBlock[2]/2 + Protrusion)])
    PolyCyl(Insert[OD],Insert[LENGTH] + Protrusion,6); // inserts
    }
    rotate([0,90,0]) // gooseneck flexy cable
    rotate(180/6)
    PolyCyl(GooseneckOD,MountBlock[0],6);
    translate([USBPlug[0]/2,0,0] + USBMating – [Protrusion/2,0,0]) // USB plug outline
    cube(USBPlug + [Protrusion,0,0],center=true);
    translate([-USBSocket[0]/2,0,0] + USBMating) // USB socket outline
    cube(USBSocket,center=true);
    translate([(Foam[0]/2 + 5*ThreadWidth),0,-(Foam[2]/2 + USBPlug[2]/2)] + USBMating – [Protrusion,0,-Protrusion]/2) // foam padding recess
    cube(Foam + [Protrusion,0,Protrusion],center=true); // foam packing
    translate([(Foam[0]/2 + 5*ThreadWidth),0, (Foam[2]/2 + USBPlug[2]/2)] + USBMating – [Protrusion,0, Protrusion]/2) // foam padding recess
    cube(Foam + [Protrusion,0,Protrusion],center=true);
    render(convexity=5)
    translate([0,0,MountBlock[2]/2 – LegendDepth])
    linear_extrude(height=LegendDepth + Protrusion) {
    translate([0,5,0])
    text(text="KE4ZNU",size=8,spacing=1.10,font="Bitstream Vera Sans:style=Bold",valign="center",halign="center");
    translate([0,-5,0])
    text(text="4 Feb 2017",size=6,spacing=1.05,font="Bitstream Vera Sans:style=Bold",valign="center",halign="center");
    }
    }
    }
    //———————-
    // Build it
    if (Layout == "Mount") {
    Mount();
    }
    if (Layout == "Show") {
    translate([0,0,-Gap/2])
    difference() {
    Mount();
    translate([0,0,MountBlock[2]])
    cube(2*MountBlock,center=true);
    }
    translate([0,0,Gap/2])
    difference() {
    Mount();
    translate([0,0,-MountBlock[2]])
    cube(2*MountBlock,center=true);
    }
    }
    if (Layout == "Build") {
    translate([0,0.6*MountBlock[1],MountBlock[2]/2])
    difference() {
    Mount();
    translate([0,0,MountBlock[2]])
    cube(2*MountBlock,center=true);
    }
    translate([0,-0.6*MountBlock[1],MountBlock[2]/2])
    rotate([180,0,0])
    difference() {
    Mount();
    translate([0,0,-MountBlock[2]])
    cube(2*MountBlock,center=true);
    }
    }
  • Proto Board Holders: 80×120 mm

    Another stack of proto boards arrived, this time 80×120 mm, and I ran off another pair of holders:

    Proto Board Holder - 80x120 - tooling
    Proto Board Holder – 80×120 – tooling

    Not wanting to, ahem, screw around with the lathe, the screws got themselves shortened the old-fashioned way: by hand, with the screw cutter, then filed and passed through a 4-40 die to clean up the threads.

    Bah!

  • Bandsaw Worklight

    Having hacked back the end of the USB gooseneck extension, a tweak of the COB LED heatsink mount for my desk lamp produces a smaller version for a 1.8 W LED:

    Chip On Board Heatsink Mount - Bandsaw Lamp - solid model
    Chip On Board Heatsink Mount – Bandsaw Lamp – solid model

    That fits half of a random heatsink, bandsawed just to the far side of the middle fin and milled flat.

    Ream out the 5 mm hole with a #8 drill for a snug fit around the gooseneck, jam gooseneck in place, dab epoxy on the corners of the recess, mash the heatsink in place, solder wires to LED, smear epoxy on the aluminum backplate, clamp while curing:

    USB Gooseneck - LED assembly
    USB Gooseneck – LED assembly

    And it looks pretty good, if I do say so myself:

    USB Gooseneck - on bandsaw
    USB Gooseneck – on bandsaw

    The hook-n-loop tape holding the cable to the bandsaw gotta go, but should suffice until I conjure a better mount.

    The alert reader may wonder how a 9 V COB LED runs from a 5 V USB cable with nary a trace of a voltage booster to be seen. Well, that’s not really a USB cable any more; I paralleled the red+white and black+green wires for lower resistance, then hacked a 9 VDC power supply into an old USB hub:

    Hacked USB hub - PCB mods
    Hacked USB hub – PCB mods

    I ripped out the upstream USB plug, hotwired the 9 V supply where the 5 V USB wires used to be, soldered jumpers on the downstream sockets to short the outer two pin pairs together, razor-knifed the power leads going into the epoxy-blobbed USB controller, and declared victory:

    Hacked USB hub - in use
    Hacked USB hub – in use

    Admittedly, that “In Use” LED runs a bit brighter now.

    I have a few other tools on that bench in need of LED lights; when I build ’em, they can all plug into this hub. No reason to invent new connectors & cables & all that. It may need a power switch.

    Turns your stomach, eh?

    The OpenSCAD source code as a GitHub Gist:

    // Chip-on-board LED light heatsink mount for desk lamp
    // Ed Nisley KE4ZNU December 2015
    // February 2017 – rectangular COB, smaller heatsink
    Layout = "Show"; // Show Build
    //- Extrusion parameters must match reality!
    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);
    //———————-
    // Dimensions
    ID = 0; // for round things
    OD = 1;
    LENGTH = 2;
    Gooseneck = [3.0,5.0,15.0]; // anchor for end of gooseneck
    COB = [30.0,11.0,2.5]; // Chip-on-board LED module
    Heatsink = [37.1,19.2,10.0]; // overall
    HeatsinkBase = 2.0; // solid base below fins
    HSLip = 1.0; // width of lip under heatsink
    BaseMargin = 2*2*ThreadWidth;
    BaseRadius = 3*ThreadThick + Gooseneck[OD]/2; // defines slab thickness
    BaseSides = 2*4;
    Base = [(Gooseneck[LENGTH] + Gooseneck[OD] + Heatsink[0] + 2*BaseRadius + BaseMargin),
    (Heatsink[1] + 2*BaseRadius + 2*BaseMargin),
    2*BaseRadius];
    echo(str("Slab thickness: ",Base[2]));
    //———————-
    // 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);
    }
    //– Lamp heatsink mount
    module Lamp() {
    difference() {
    translate([(Base[0]/2 – BaseRadius – Gooseneck[LENGTH]),0,0])
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(Base[0]/2 – BaseRadius),j*(Base[1]/2 – BaseRadius),Base[2]/2])
    sphere(r=BaseRadius/cos(180/BaseSides),$fn=BaseSides);
    translate([(Heatsink[0]/2 + Gooseneck[OD]), // main heatsink recess
    0,
    (Base[2] + Heatsink[2]/2 – HeatsinkBase)])
    cube((Heatsink + [HoleWindage,HoleWindage,0.0]),center=true);
    translate([(Heatsink[0]/2 + Gooseneck[OD]),0,HeatsinkBase]) // lower lip to shade lamp module
    scale([1,1,2])
    cube(Heatsink – [2*HSLip,2*HSLip,0],center=true);
    translate([0,0,Base[2]/2]) // goooseneck insertion
    rotate([0,-90,0]) rotate(180/8)
    PolyCyl(Gooseneck[OD],Base[0],8);
    translate([0,0,Base[2]/2 + Gooseneck[ID]/2]) // wire exit
    rotate([180,0,0])
    PolyCyl(Gooseneck[ID],Base[2],6);
    translate([Gooseneck[OD],0,(Base[2] – HeatsinkBase – Protrusion)/2]) // wire slot
    rotate([180,0,0])
    cube([2*Gooseneck[OD],Gooseneck[ID],(Base[2] – HeatsinkBase + Protrusion)],center=true);
    }
    }
    //———————-
    // Build it
    if (Layout == "Show") {
    Lamp();
    }
    if (Layout == "Build") {
    }

     

  • 60 kHz Preamp: Board Holder

    A cleaned up version of my trusty circuit board holder now keeps the 60 kHz preamp off what passes for a floor in the attic:

    Preamp in attic
    Preamp in attic

    The solid model became slightly taller than before, due to a serious tangle of wiring below the board, with a narrower flange that fits just as well in the benchtop gripper:

    Proto Board - 80x110
    Proto Board – 80×110

    Tidy brass inserts epoxied in the corners replace the previous raw screw holes in the plastic:

    Proto Board Holder - 4-40 inserts and screws
    Proto Board Holder – 4-40 inserts and screws

    The screws standing on their heads have washers epoxied in place, although that’s certainly not necessary; the dab of left-over epoxy called out for something. The screws got cut down to 7 mm after curing.

    The preamp attaches to a lumpy circle of loop antenna hung from the rafters and returns reasonable results:

    WWVB - morning - 2017-01-16
    WWVB – morning – 2017-01-16

    The OpenSCAD source code as a GitHub Gist:

    // Test support frame for proto boards
    // Ed Nisley KE4ZNU – Jan 2017
    ClampFlange = true;
    Channel = false;
    //- Extrusion parameters – must match reality!
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1;
    HoleWindage = 0.2;
    //- Screw sizes
    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;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Insert = [3.9,4.6,5.8];
    //- PCB sizes
    PCBSize = [110.0,80.0,1.5];
    PCBShelf = 2.0;
    Clearance = 2*[ThreadWidth,ThreadWidth,0];
    WallThick = 5.0;
    FrameHeight = 10.0;
    ScrewOffset = 0.0 + Clear4_40/2;
    ScrewSites = [[-1,1],[-1,1]]; // -1/0/+1 = left/mid/right and bottom/mid/top
    OAHeight = FrameHeight + Clearance[2] + PCBSize[2];
    FlangeExtension = 3.0;
    FlangeThick = IntegerMultiple(2.0,ThreadThick);
    Flange = PCBSize
    + 2*[ScrewOffset,ScrewOffset,0]
    + 2*[Washer4_40OD,Washer4_40OD,0]
    + [2*FlangeExtension,2*FlangeExtension,(FlangeThick – PCBSize[2])]
    ;
    echo("Flange: ",Flange);
    NumSides = 4*5;
    WireChannel = [Flange[0],15.0,3.0 + PCBSize[2]];
    WireChannelOffset = [Flange[0]/2,25.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);
    FixDia = Dia / cos(180/Sides);
    cylinder(r=(FixDia + HoleWindage)/2,h=Height,$fn=Sides);
    }
    //- Build it
    difference() {
    union() { // body block
    translate([0,0,OAHeight/2])
    cube(PCBSize + Clearance + [2*WallThick,2*WallThick,FrameHeight],center=true);
    for (x=[-1,1], y=[-1,1]) { // screw bosses
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    }
    if (ClampFlange) // flange for work holder
    linear_extrude(height=Flange[2])
    hull()
    for (i=[-1,1], j=[-1,1]) {
    translate([i*(Flange[0]/2 – Washer4_40OD/2),j*(Flange[1]/2 – Washer4_40OD/2)])
    circle(d=Washer4_40OD,$fn=NumSides);
    }
    }
    for (x=[-1,1], y=[-1,1]) { // screw position indexes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    -Protrusion])
    rotate(x*y*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6); // screw clearance holes
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2] – Insert[LENGTH]])
    rotate(x*y*180/(2*6))
    PolyCyl(Insert[OD],Insert[LENGTH] + Protrusion,6); // inserts
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2]])
    PolyCyl(1.2*Washer4_40OD,(PCBSize[2] + Protrusion),NumSides); // washers
    }
    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);
    }
  • Loop Antenna Splice Reinforcement

    Those solder joints and finicky little wires seem much too fragile on their own:

    LF Loop Antenna - complete joint
    LF Loop Antenna – complete joint

    This should help:

    Loop Antenna Splice - assembled
    Loop Antenna Splice – assembled

    Foam blocks hold the ribbon cable in place and provide a bit of strain relief around the hard plastic edge:

    Loop Antenna Splice - hardware
    Loop Antenna Splice – hardware

    The brass inserts in the bottom block (on the left) got epoxied in place, because they must provide quite a bit of force to clamp the foam. Their larger knurled end sits flush with the outside surface and the smaller end has one thread thickness of clearance below the inner surface.

    A last look at the wiring:

    Loop Antenna Splice - wiring
    Loop Antenna Splice – wiring

    I think the preamp must sit at some distance from the antenna to prevent feedback, but that remains to be seen.

    The M2’s nozzle accumulated a huge blob of PETG that turned into a giant smear:

    Loop Antenna Splice - PETG booger
    Loop Antenna Splice – PETG booger

    Fortunately, it’s on the inside where nobody will ever see it. If you know where to look, it’s barely visible from the outside.

    The solid model shows off the structure a bit better:

    Loop Antenna Splice - show view
    Loop Antenna Splice – show view

    The inside view:

    Loop Antenna Splice - bottom
    Loop Antenna Splice – bottom

    The OpenSCAD source code as a GitHub Gist:

    // Ribbon cable loop antenna splice
    // Ed Nisley KE4ZNU December 2016
    Layout = "Text";
    //- Extrusion parameters must match reality!
    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);
    //———-
    // Dimensions
    Cable = [200,48.0,1.5]; // X = longer than anything else
    Splice = [15.0,53.0,5.0]; // epoxy blob around joints
    Foam = [15.0,Splice[1],2.0];
    CornerRadius = 5.0;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    Insert = [3.9,4.6 – 0.1,5.8]; // 4-40 knurled brass insert
    Screw = [2.7,5.5,2.0]; // OD = head LENGTH = head thickness
    Washer = [3.0,8.0,0.8];
    BlockOA = [60.0, // convenient length
    Splice[1] + 4*Washer[OD], // clearance around washer on top
    2*(Insert[LENGTH] + 2*ThreadThick)]; // insert sets both thicknesses
    NumScrews = 2; // screws along each side of cable
    ScrewOC = [BlockOA[0] / NumScrews,
    BlockOA[1] – 2*Washer[OD],
    2*BlockOA[2] // ensure complete holes
    ];
    TextThick = 3*ThreadThick; // depth of text into surface
    TextFit = HoleWindage/2; // clearance around text polygons
    //———————-
    // 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(d=(FixDia + HoleWindage),h=Height,$fn=Sides);
    }
    //—–
    // Blocky model of cable + splice + wire tap for subtraction
    module Antenna() {
    union() {
    cube(Cable,center=true);
    cube(Splice,center=true);
    for (i=[-1,1])
    translate([0,-Splice[1]/2,0])
    cube([Splice[0]/2,Splice[1],2*Foam[2]],center=true);
    }
    }
    // Outside shape of splice Block, less screw clearance
    module SpliceBlock() {
    difference() {
    hull()
    for (i=[-1,1], j=[-1,1])
    translate([i*(BlockOA[0]/2 – CornerRadius),j*(BlockOA[1]/2 – CornerRadius),-BlockOA[2]/2])
    cylinder(r=CornerRadius,h=BlockOA[2],$fn=4*8);
    for (i = [0:NumScrews – 1], j=[-1,1])
    translate([-BlockOA[0]/2 + ScrewOC[0]/2 + i*ScrewOC[0],j*ScrewOC[1]/2,-(BlockOA[2]/2 + Protrusion)])
    PolyCyl(Screw[ID],BlockOA[2] + 2*Protrusion,6);
    }
    }
    // Splice block less cable
    module ShapedBlock() {
    difference() {
    SpliceBlock();
    Antenna();
    }
    }
    // Bottom
    module BottomPlate() {
    difference() {
    ShapedBlock();
    translate([0,0,BlockOA[2]/2])
    cube(BlockOA + 2*[Protrusion,Protrusion,0],center=true);
    Antenna(Splice);
    for (i = [0:NumScrews – 1], j=[-1,1])
    translate([-BlockOA[0]/2 + ScrewOC[0]/2 + i*ScrewOC[0],j*ScrewOC[1]/2,-(BlockOA[2]/2 + Protrusion)])
    PolyCyl(Insert[OD],2*Insert[LENGTH],6);
    for (i=[-1,1])
    translate([i*((BlockOA[0] – Foam[0] + Protrusion)/2),0,(BlockOA[2]/2 – Cable[2]/2 – Foam[2])])
    cube([Foam[0] + Protrusion,Foam[1],BlockOA[2]],center=true);
    }
    }
    // Top
    module TopPlate() {
    difference() {
    ShapedBlock();
    translate([0,0,-BlockOA[2]/2])
    cube(BlockOA + 2*[Protrusion,Protrusion,0],center=true);
    Antenna(Splice);
    for (i=[-1,1])
    translate([i*((BlockOA[0] – Foam[0] + Protrusion)/2),0,-(BlockOA[2]/2 – Cable[2]/2 – Foam[2])])
    cube([Foam[0] + Protrusion,Foam[1],BlockOA[2]],center=true);
    rotate(90) {
    translate([0,6,BlockOA[2]/2 – TextThick])
    TextHack("KE4ZNU",8,0.0,1.15,TextThick + Protrusion);
    translate([0,-6,BlockOA[2]/2 – TextThick])
    TextHack("2016·12",6,0.0,1.20,TextThick + Protrusion);
    }
    }
    }
    module TextHack(Text="sample",Size=10,Offset=0.0,Space=1.0,Thick=ThreadThick) {
    linear_extrude(height=Thick,convexity=10)
    offset(r=Offset)
    text(Text,font=":bold",size=Size,spacing=Space,halign="center",valign="center");
    }
    //———-
    // Build them
    if (Layout == "Antenna")
    Antenna();
    if (Layout == "SpliceBlock")
    SpliceBlock();
    if (Layout == "ShapedBlock")
    ShapedBlock();
    if (Layout == "Bottom")
    BottomPlate();
    if (Layout == "Top")
    TopPlate();
    if (Layout == "Text") {
    translate([0,6,0])
    TextHack("KE4ZNU",8,-TextFit,1.15,TextThick);
    translate([0,-6,0])
    TextHack("2016·12",6,-TextFit,1.20,TextThick);
    }
    if (Layout == "Show") {
    translate([0,0,5])
    TopPlate();
    translate([0,0,-5])
    BottomPlate();
    color("Orange",0.2)
    Antenna();
    }
    if (Layout == "Build") {
    translate([0,-0.6*BlockOA[1],BlockOA[2]/2])
    rotate([180,0,0])
    TopPlate();
    translate([0,0.6*BlockOA[1],BlockOA[2]/2])
    BottomPlate();
    }