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.

Author: Ed

  • LF Crystal Tester: Pretty Plots

    A slight modification spits out the (actual) frequency and dBV response (without subtracting the 108 dB intercept to avoid negative numbers for now) to the serial port in CSV format, wherein a quick copypasta into a LibreOffice Calc spreadsheet produces this:

    Spectrum-32
    Spectrum-32

    Changing the center frequency and swapping in a 60 kHz resonator:

    Spectrum-60
    Spectrum-60

    Much prettier than the raw scope shot with the same data, there can be no denyin’:

    Log V vs F - 32766 4 Hz - CX overlay
    Log V vs F – 32766 4 Hz – CX overlay

    I think the wobbulations around the parallel resonant dip come from the excessively hugely too large 10 µF caps in the signal path, particularly right before the log amp input, although the video bandwidth hack on the AD8310 module may contribute to the problem. In any event, I can see the log amp output wobbling for about a second, which is way too long.

    Anyhow, the series-resonant peaks look about 1 Hz wide at the -3 dBV points, more or less agreeing with what I found with the HP 8591 spectrum analyzer. The series cap is a bit smaller, producing a slightly larger frequency change in the series resonant frequency: a bit under 2 Hz, rather than the 1 Hz estimated with the function generator and spectrum analyzer.

    I still don’t understand why the parallel resonant dip changes, although I haven’t actually done the pencil pushing required for true understanding.

    Ain’t they lovely, though?

  • Yellowbook Do-Not-Deliver Delivery Option

    Last summer I followed the procedure that should turn off (one of) the ersatz “Yellow Pages” directories littering our driveway.

    This just arrived:

    YellowBook Do-Not-Deliver Delivery
    YellowBook Do-Not-Deliver Delivery

    Seeing as how they have the wrong town in my address, there’s still another excuse available.

    My guess: they’re paid by tonnage of books delivered and have a powerful incentive to continue delivering all of them, no matter what gets in the way.

    You absolutely cannot make this stuff up.

  • Amazon Packaging

    The ample padding around this bag of fragile pecans leaves nothing to be desired:

    Amazon - well-packed pecans
    Amazon – well-packed pecans

    They’re firmly held in place on all sides, well protected from injury, and survived their shipping ordeal unscathed: not a bruise or break to be found. Well done!

    That’s not always the case. A padded envelope recently arrived with an obvious wound:

    Amazon - envelope perforations
    Amazon – envelope perforations

    Which came from its completely unprotected contents:

    Amazon - unprotected PCB pins
    Amazon – unprotected PCB pins

    Fortunately, the fragile glass front plate of that OLED managed to put itself flat against a small box inside the otherwise empty bag. it wasn’t broken, but due only to good fortune.

    “Static sensitive parts enclosed”, indeed …

  • Mailing Tube End Caps

    Faced with a need to send documents rolled up in a tube, rather than folded flat, I sawed off a suitable length of cardboard tube from the heap, then discovered a distinct lack of end caps.

    Well, once again, it’s 3D printing to the rescue:

    Mailing Tube Cap - top - Slic3r
    Mailing Tube Cap – top – Slic3r

    The small ribs probably don’t actually do anything, but seemed like a nice touch.

    They’re somewhat less boring from the bottom:

    Mailing Tube Cap - bottom - Slic3r
    Mailing Tube Cap – bottom – Slic3r

    The fancy spider supports that big flat top and provides some crush resistance. The flat flange should collect the edge of the packing tape wrapped around the ends.

    A firm shove installs them, so the size worked out perfectly:

    Mailing tube end cap - installed
    Mailing tube end cap – installed

    Add a wrap of tape to each end, affix the USPS label, and they went out with the next day’s mail, PETG hair and all.

    The OpenSCAD source code as a GitHub Gist:

    // Mailing tube end cap
    // Ed Nisley KE4ZNU – June 2017
    Layout = "Build";
    //- 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;
    TubeID = 2 * inch;
    TubeWall = 0.1 * inch;
    CapInsert = 15.0;
    CapRim = 1.0;
    CapWall = 3*ThreadWidth;
    NumFlanges = 3;
    FlangeHeight = 3*ThreadThick;
    FlangeWidth = ThreadWidth/2;
    FlangeSpace = CapInsert / (NumFlanges + 1);
    OAHeight = CapInsert + CapRim;
    NumRibs = 3*4;
    NumSides = 3*NumRibs;
    //- 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);
    }
    module TubeCap() {
    difference() {
    cylinder(d=TubeID,h=OAHeight,$fn=NumSides);
    translate([0,0,CapWall])
    cylinder(d=TubeID – 2*CapWall,h=OAHeight,$fn=NumSides);
    }
    for (i=[1:NumFlanges])
    translate([0,0,i*FlangeSpace])
    difference() {
    cylinder(d=TubeID + 2*FlangeWidth,h=FlangeHeight,$fn=NumSides);
    translate([0,0,-Protrusion])
    cylinder(d=TubeID – 2*CapWall,h=FlangeHeight + 2*Protrusion,$fn=NumSides);
    }
    for (i=[0:NumRibs-1])
    rotate(i*360/NumRibs)
    translate([0,-ThreadWidth,CapWall + ThreadThick])
    cube([TubeID/2 – CapWall/2,2*ThreadWidth,CapInsert + CapRim – CapWall – ThreadThick],center=false);
    translate([0,0,CapInsert]) {
    difference() {
    cylinder(d=TubeID + 2*TubeWall,h=CapRim,$fn=NumSides);
    translate([0,0,-Protrusion])
    cylinder(d=TubeID – 3*2*CapWall,h=2*CapRim,$fn=NumSides);
    }
    }
    }
    //- Build things
    if (Layout == "Show")
    TubeCap();
    if (Layout == "Build")
    translate([0,0,OAHeight])
    rotate([180,0,0])
    TubeCap();
  • Monthly Image: Great Blue Heron

    This Great Blue Heron caught a bright orange goldfish in the Vassar Farm Pond just before I rode past, spotted the scene, and fumbled my camera out of the underseat bag.

    The heron hurked the fish down, with the abrupt right-angle bend in its neck marking the fish’s current location:

    Great Blue Heron - swallowing
    Great Blue Heron – swallowing

    A bit of wiggling & jiggling put the meal in the right place and the bird relaxed:

    Great Blue Heron - ruminating
    Great Blue Heron – ruminating

    A postprandial flight around the pond apparently settled the fish:

    Great Blue Heron - takeoff
    Great Blue Heron – takeoff

    It landed on a snag a few dozen feet from where it started, then proceeded to look regal:

    Great Blue Heron - idling
    Great Blue Heron – idling

    Those things really do look like pterodactyls in flight!

     

  • LF Crystal Tester: First Light!

    After adding a MAX4165 buffer amp to drive the crystal test fixture at 1 µW and a MAX4255 to amplify the 1 mV crystal output by 40 dB, then removing the AD8310 log amp module’s 50 Ω terminator to better match the MAX4255’s output drive ability, this happened:

    Log V vs F - 32766 4 Hz - CX overlay
    Log V vs F – 32766 4 Hz – CX overlay

    That’s:

    • A 32.768 kHz quartz resonator
    • A ±2 Hz span centered on 32.766 kHz
    • 0.10 Hz frequency steps
    • The 22 pF cap out / in circuit (left & right peaks, respectively)
    • Log amp output at 24 mV/dBV, with a nominal -108 dBV intercept at 0 V

    With a 4 Hz span and 0.1 Hz steps, you get only 41 samples along the X axis: it’s supposed to look spotty.

    The 2.2 V response at the top of the left peak corresponds to 2.2 / 24 mV/dBV = 91.7 dBV, then you knock off the -108 dBV intercept to get -16.3 dBV. The valley at 1.88 V is 78.3 – 108 = -29.7 dBV, down about 13 dBV from the corresponding peak. The peak-to-baseline over on the right looks like 200 mV = 8 dBV.

    The AD8310 datasheet uses “intercept” in a manner I had not previously encountered. They plot the AD8310 output in volts against the input signal level in dBV, with the “intercept” marking the extrapolated point where the straight line with slope 24 mV/dBV crosses the X axis: the equation is volts = slope*(input dBV – intercept dBV). Back in the day, I learned the intercept was where the line crossed the Y axis at X=0, so the straight-line equation was simply y = slope*x + intercept. Took me a while to figure that out.

    Then subtract the 40 dB gain from the crystal output to the log amp to get -56 dbV = 1.6 mV. That’s close enough to the 1 mV before adding the MAX4255. All those numbers seem slightly squishy, but they’re close enough.

    The peaks are 13-ish spots apart, which corresponds to 1.3 Hz, which is roughly the 1 Hz I measured with the HP8591 spectrum analyzer. The baseline is down 8 dBV, not quite as much as the analyzer’s 13 dB at 1 Hz offset from the peaks.

    What’s not right: the parallel-resonant dip to the right of each peak should be at the same frequency for both traces, because it doesn’t vary with added series capacitance, but it’s pretty much tracking the series-resonant peak frequency.

    The amount of noise on the log amp output looks like 50 mV = 2 dBV. That’s a lot, compared to the 13 dBV response, but some judicious averaging may save the day.

    The 22 MHz GBW of the MAX4255 rolls off the high end at 220 kHz. I AC coupled the signal chain with 10 µF dipped tantalum caps from my lifetime supply, which may pass entirely too much of the low end; the settling time is way too long. This probably requires smaller caps and maybe an actual bandpass filter.

    The 50 mV-ish noise on the DAC output driving the X axis suggests my proto board layout isn’t up to the demands of this circuit: there shouldn’t be any noise in that direction.

    Some poking around suggests the OLED display is way noisier than you’d (well, I’d) expect. The faded-out lower section in the picture below suggests it’s refreshing one line = 128 pixels at a time. More study is indicated.

    But, if you squint hard enough, this lashup produces numbers in the right ballpark. Given that it’s a collection of cheap-as-dirt eBay modules flying in formation, that’s nothing to sniff at:

    Crystal Tester - First Light
    Crystal Tester – First Light

    Those “gold tone” SMA connectors really make it look like serious RF hardware, don’t they? [grin]

    The round twiddlepot floating on the white pillow trims the DDS output voltage by a factor of two = 6 dB. Combined with the 0-6-12-18 dB gain steps provided by the header in front of the MAX4165 (to the right of the pillow), you can set the drive voltage so the crystal gets (roughly) its rated 1 µW maximum drive power.

  • Proto Board Holder: Revised Screw Mounts

    Improving the crystal tester’s (nonexistent) grounding requires a band of copper tape around the inside of the proto board holder. Rather than cut the tape lengthwise to fit the holder, a new one will be just tall enough:

    Proto Board - 80x120 - revised inserts - Slic3r
    Proto Board – 80×120 – revised inserts – Slic3r

    While I was at it, I deleted the washer recesses, because those didn’t work out well, and fiddled the screw holes to put the inserts in from the bottom:

    Proto Board - 80x120 - revised inserts - detail - Slic3r
    Proto Board – 80×120 – revised inserts – detail – Slic3r

    Although the overhang inside the holes will be ugly, I’ll epoxy the inserts flush with the bottom and nobody will ever know.

    The copper tape now makes a tidy ground strap:

    Crystal Tester - ground strap - rear
    Crystal Tester – ground strap – rear

    With a gap in the front to eliminate the obvious loop:

    Crystal Tester - ground strap - front gap
    Crystal Tester – ground strap – front gap

    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
    Layout = "Frame";
    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 = [80.0,120.0,1.6];
    PCBShelf = 1.0; // support rim under PCB
    Clearance = 2*[ThreadWidth,ThreadWidth,0];
    WallThick = 4.0;
    FrameHeight = IntegerMultiple(3/8 * inch,1.0);
    echo(str("Inner height: ",FrameHeight));
    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];
    echo(str("OAH: ",OAHeight));
    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(str("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 things
    if (Layout == "Frame")
    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),
    -Protrusion])
    rotate(x*y*180/(2*6))
    PolyCyl(Insert[OD],OAHeight – PCBSize[2] – 3*ThreadThick + Protrusion,6); // inserts
    if (false)
    translate([x*(PCBSize[0]/2 + ScrewOffset),
    y*(PCBSize[1]/2 + ScrewOffset),
    OAHeight – PCBSize[2]])
    PolyCyl(1.2*Washer4_40OD,(PCBSize[2] + Protrusion),NumSides); // washer recess
    }
    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 (x=[-1,1]) // bosses around screws
    translate([x*(PCBSize[0]/2 + ScrewOffset),0,0])
    cylinder(r=Washer4_40OD,h=OAHeight,$fn=NumSides);
    for (x=[-1,1]) // frame screw holes
    translate([x*(PCBSize[0]/2 + ScrewOffset),0,-Protrusion])
    rotate(x*180/(2*6))
    PolyCyl(Clear4_40,(OAHeight + 2*Protrusion),6);
    for (x=[-1,1]) // PCB insert holes
    translate([x*PCB2OC/2,(Washer4_40OD + Protrusion),OAHeight/2])
    rotate([90,0,0])
    cylinder(d=PCB2Insert[OD],h=2*(Washer4_40OD + Protrusion),$fn=6);
    }