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

  • AD9850 DDS Module: 125 MHz Oscillator vs. Temperature, Linear Edition

    A day of jockeying the AD9850 DDS oscillator shows an interesting relation between the frequency offset and the oscillator temperature:

    DDS Oscillator Frequency Offset vs. Temperature - complete
    DDS Oscillator Frequency Offset vs. Temperature – complete

    Now, as it turns out, the one lonely little dot off the line happened just after I lit the board up after a tweak, so the oscillator temperature hadn’t stabilized. Tossing it out produces a much nicer fit:

    DDS Oscillator Frequency Offset vs. Temperature
    DDS Oscillator Frequency Offset vs. Temperature

    Looks like I made it up, doesn’t it?

    The first-order coefficient shows the frequency varies by -36 Hz/°C. The actual oscillator frequency decreases with increasing temperature, which means the compensating offset must become more negative to make the oscillator frequency variable match reality. In previous iterations, I’ve gotten this wrong.

    For example, at 42.5 °C the oscillator runs at:

    125.000000 MHz - 412 Hz = 124.999588 MHz

    Dividing that into 232 = 34.35985169 count/Hz, which is the coefficient converting a desired frequency into the DDS delta phase register value. Then, to get 10.000000 MHz at the DDS output, you multiply:
    10×106 × 34.35985169 = 343.598517×106

    Stuff that into the DDS and away it goes.

    Warmed half a degree to 43.0 °C, the oscillator runs at:

    125.000000 MHz - 430 Hz = 124.999570 MHz

    That’s 18 Hz lower, so the coefficient becomes 34.35985667, and the corresponding delta phase for a 10 MHz output is 343.598567×106.

    Obviously, you need Pretty Good Precision in your arithmetic to get those answers.

    After insulating the DDS module to reduce the effect of passing breezes, I thought the oscillator temperature would track the ambient temperature fairly closely, because of the more-or-less constant power dissipation inside the foam blanket. Which turned out to be the case:

    DDS Oscillator Temperature vs. Ambient
    DDS Oscillator Temperature vs. Ambient

    The little dingle-dangle shows startup conditions, where the oscillator warms up at a constant room temperature. The outlier dot sits 0.125 °C to the right of the lowest pair of points, being really conspicuous, which was another hint it didn’t belong with the rest of the contestants.

    So, given the ambient temperature, the oscillator temperature will stabilize at 0.97 × ambient + 20.24, which is close enough to a nice, even 20 °C hotter.

    The insulation blanket reduces short-term variations due to breezes, which, given the -36 Hz/°C = 0.29 ppm temperature coefficient, makes good sense; you can watch the DDS output frequency blow in the breeze. It does, however, increase the oscillator temperature enough to drop the frequency by 720 Hz, so you probably shouldn’t use the DDS oscillator without compensating for at least its zero-th order offset at whatever temperature you expect.

    Of course, that’s over a teeny-tiny temperature range, where nearly anything would be linear.

    The original data:

    DDS Oscillator offset vs temperature - 2017-06-24
    DDS Oscillator offset vs temperature – 2017-06-24
  • Canon LiDE 120 Scanner vs. SANE

    I just replaced a cheap old Canon LiDE 30 flatbed scanner with a cheap new LiDE 120, only to get flat-black scans. The machinery worked (yes, I released the travel lock), everything seemed fine, the images were the proper size, but they were dead black.

    Of course, the scanner worked OK on the Token Windows Box, but wow what crappy software they include.

    Turns out the LiDE 120 requires the latest-and-greatest version 1.0.27 of the various SANE programs & libraries. Mercifully, getting those didn’t require compiling from source, just setting up the maintainer’s PPA of the most recent stable release:

    sudo add-apt-repository ppa:rolfbensch/sane-release
    sudo apt-get update
    sudo apt-get upgrade
    

    Which introduced circular dependencies with the distro-installed version 1.0.25 files, which I solved by ripping the entire SANE Thing out by the root(s) and reinstalling it to (re)synchronize All The Things:

    sudo apt-get remove libsane:i386 sane sane-utils xsane libsane-common ia32-libs libsane
    sudo apt-get install libsane:i386 sane sane-utils xsane libsane-common ia32-libs libsane
    

    And then It Just Worked:

    C-Note - detail
    C-Note – detail

    Of course, you must keep this WARNING in mind:

    Canon LiDE 120 - Legal Issues Warning
    Canon LiDE 120 – Legal Issues Warning

    Franklin didn’t know about scanners or color laser printers when he observed:

    Those who would give up essential Liberty, to purchase a little temporary Safety, deserve neither Liberty nor Safety

    Of course, there’s more to the story, but one should:

    Never let the truth get in the way of a good story.

  • LM75A vs. SOIC Adapter: Mirror Imaging

    After hairballing an LM75A I²C temperature sensor to verify at least one of the eBay lot worked, a bag of SOIC-to-DIP space transformers arrived, so I soldered up another LM75A:

    LM75A on DIP8 adapter - top
    LM75A on DIP8 adapter – top

    The SOIC chip pattern sits at right angles to the DIP pins, which took some getting used to.

    The slightly defocused wire connecting pin 4 (on the IC) to pins 5, 6, and 7 (on the PCB) selects address 0x48.

    So I flipped it over, soldered four wires (+5 V, GND, SDA, SCL) to the numbered pins on bottom of the board, made up a little header for the other end, wired a socket strip on the crystal tester board, plugged it in, and … nothing worked.

    Turns out that the other side of the board carries a TSSOP pattern, which I’d neatly masked off with a snippet of Kapton tape, surrounded by eight numbered pins. Of course, those pin numbers correspond to the TSSOP pattern facing you, so they’re mirror-imaged for the SOIC pattern on the other side.

    Soooo, the proper wiring for the SOIC pattern as seen from the TSSOP side has the pin numbers exactly bass-ackwards:

    LM75A on DIP8 adapter - bottom
    LM75A on DIP8 adapter – bottom

    The insulation looked a lot better the first time I soldered the wires to the PCB. Honest.

    Anyhow, when correctly wired, the LM75A worked as it should:

    LM75A Temperature Sensor - installed
    LM75A Temperature Sensor – installed

    It’s snuggled chip-down against the top of the 125 MHz oscillator can, with a dab of heatsink compound improving their thermal bond and a yellow cable tie around the foam holding them together. The socket header is wired pin-for-pin to the DAC I²C socket directly above it.

    The OLED temperature display shows 28.250 °C, because the oscillator just started up in a cool basement. It’ll eventually settle around 39-ish °C, where its output should be pretty close to the 125 MHz – 344 Hz value hardcoded into the source.

    Oh, that’s a 3 mm amber LED next to the relay can: much less glaring than the white LED, no matter what it looks like here.

  • Mailing Tube End Caps: Screw-in Version

    The mailing tube arrived with contents intact, although the USPS inlet scanning didn’t work and the tube pretty much teleported across several states without leaving any tracking data behind. The recipient suggested several modifications to the caps:

    Review of user experience of tube end:
    The ribs on the endcap are very good at holding the cap on, so much so that I had to use a prying implement to remove it, which cracked the flange.
    Would consider less depth on the cap, and possibly another layer on the flange.

    Some continuous process improvement (a.k.a OpenSCAD hackage) produced a swoopy threaded cap with thumb-and-finger grips:

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

    The finger grips are what’s left after stepping a sphere out of the cap while rotating it around the middle:

    Mailing Tube Cap - finger grip construction
    Mailing Tube Cap – finger grip construction

    That worked out surprisingly well, with the deep end providing enough of a vertical-ish surface to push against.

    The two hex holes fit a pin wrench, because the grips twist only one way: outward. The wrench eliminates the need for a flange, as you can now adjust the cap insertion before slathering packing tape over the ends. Man, I loves me some good late binding action!

    A three-start thread seemed like overkill, but was quick & easy. The “thread form” consists of square rods sunk into the cap perimeter, with one edge sticking out:

    Mailing Tube Cap - thread detail
    Mailing Tube Cap – thread detail

    They’re 1.05 times longer than the cap perimeter facets to make their ends overlap, although they’re not tapered like the ones in the broom handle dingus, because it didn’t (seem to) make any difference to the model’s manifoldhood.

    Not needing any endcaps right now, I built one for show-n-tell:

    Threaded mailing tube end cap - installed
    Threaded mailing tube end cap – installed

    The OpenSCAD source code as a GitHub Gist:

    // Mailing tube end cap
    // Ed Nisley KE4ZNU – June 2017
    Layout = "Build";
    Model = "Screw";
    //- 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 = 6*ThreadThick;
    CapWall = 3*ThreadWidth;
    NumFlanges = 3;
    FlangeHeight = 3*ThreadThick;
    FlangeWidth = ThreadWidth/2;
    FlangeSpace = CapInsert / (NumFlanges + 1);
    ThumbHoleOD = 20.0;
    ThumbHoleAngle = 100;
    ThumbHoleSteps = 10;
    SpannerPinOD = 5.0;
    HelixOD = 4*ThreadThick;
    HelixHeight = 0.75*CapInsert;
    HelixAngle = atan(HelixHeight/(PI*TubeID));
    HelixStarts = 3;
    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 ScrewCap() {
    union() {
    difference() {
    cylinder(d=TubeID,h=OAHeight,$fn=NumSides);
    for (a=[0,180])
    for (i=[0:ThumbHoleSteps-1])
    rotate(a + i*ThumbHoleAngle/ThumbHoleSteps)
    translate([TubeID/4,0,-i*ThumbHoleOD/(2*ThumbHoleSteps)])
    sphere(d=ThumbHoleOD);
    for (a=[0,180])
    rotate(a – 60)
    translate([0.75*TubeID/2,0,-Protrusion])
    rotate(0*180/6)
    PolyCyl(SpannerPinOD,0.75*CapInsert,6);
    }
    for (s=[0:HelixStarts-1])
    for (i=[0:NumSides-1])
    rotate(i*360/NumSides + 180/NumSides + s*360/HelixStarts)
    translate([TubeID/2 – 0.25*HelixOD,0,i*HelixHeight/NumSides + HelixOD])
    rotate([90 + HelixAngle,0,0])
    cylinder(d=HelixOD,h=1.05*PI*TubeID/NumSides,center=true,$fn=4);
    }
    }
    module PushCap() {
    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 (Model == "Push")
    if (Layout == "Show")
    PushCap();
    else if (Layout == "Build")
    translate([0,0,OAHeight])
    rotate([180,0,0])
    PushCap();
    if (Model == "Screw")
    if (Layout == "Show")
    ScrewCap();
    else if (Layout == "Build")
    translate([0,0,OAHeight])
    rotate([180,0,0])
    ScrewCap();
  • Handbag Strap Rivet Repair

    One of the leather strap anchors on Mary’s giant haul-everything-to-a-concert(*) handbag pulled its rivet through the canvas fabric:

    Handbag - pulled-through rivet
    Handbag – pulled-through rivet

    We knotted the strap around the zippered opening and completed the mission.

    Of course, it wouldn’t have pulled through if they’d splurged on washers, but noooo too expensive:

    Handbag - intact rivet - inside
    Handbag – intact rivet – inside

    Some rummaging produced a pan-head M3 screw of suitable length:

    Handbag - repaired - outside
    Handbag – repaired – outside

    A slightly battered acorn nut was a special treat for the inside, with another washer to keep me happy:

    Handbag - repaired - inside
    Handbag – repaired – inside

    That was easy!

    (*) At Tanglewood, where they don’t strip-search you on the way in, tow-behind coolers seemed de rigueur, and a good time was had by all.

  • LF Crystal Tester: Bring the Noise!

    The OLED display refresh contributes 100 Hz noise pulses to the low-level sine wave from the crystal test fixture:

    OLED Enabled - 100 Hz display refresh
    OLED Enabled – 100 Hz display refresh

    Disabling the display by activating its powersave option reveals 60 Hz pulses from the USB port on the Arduino Nano:

    OLED Powersave - 60 Hz USB Ground Loop
    OLED Powersave – 60 Hz USB Ground Loop

    Unplugging the USB cable, leaving just the +5 VDC power supply and coax cable to the oscilloscope, solves most of the problem:

    OLED Powersave - USB unplugged
    OLED Powersave – USB unplugged

    A closer look shows some (relatively) low frequency noise remains in full effect:

    OLED Powersave - USB unplugged - detail
    OLED Powersave – USB unplugged – detail

    Disabling the display while measuring the crystal seems sensible, although, to avoid surprises, a pushbutton should start the process. Unplugging the USB port puts a real crimp in the data collection, although that’s probably survivable with a USB isolator, one of which is on the way around the planet.

    The remaining low-level chop requires more thought. Somewhat to my surprise, holding the Arduino Reset button down doesn’t change much of anything, so it’s not a firmware thing.

    Those 10 µF coupling caps gotta go.

    With the OLED dark and the USB carrying data:

    Spectrum - OLED Powersave - USB in
    Spectrum – OLED Powersave – USB in

    Compare that to the first pass:

    Spectrum-60
    Spectrum-60

    Tamping down the noise seems to reduce the overall amplitude variation, but it also makes the capacitor-in and capacitor-out curves more consistent. There may be other things going on that I haven’t accounted for.

    The peak frequencies differ by 0.2 Hz, which is probably due to a few degrees of temperature difference. Obviously, it’s badly in need of a temperature calibration & correction.

  • 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?