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.

Category: Electronics Workbench

Electrical & Electronic gadgets

  • Switch Contact Bounce

    An Arduino hairball for an upcoming Digital Machinist column:

    Arduino UNO clone - test setup
    Arduino UNO clone – test setup

    A short program monitors the switch. When it closes, the program reads the analog voltage from the pot and blinks the LED (on Pin 13, so you don’t need an external LED) for that number of milliseconds.

    Some diligent rummaging produced a spectacularly bouncy switch (lower trace) with the output pulse (upper trace):

    Contact Bounce - Matsuhita - NO 1
    Contact Bounce – Matsuhita – NO 1

    A longer timebase shows it’s rattling around for nearly half a millisecond:

    Contact Bounce - Matsuhita - NO 2
    Contact Bounce – Matsuhita – NO 2

    The second pulse in the upper trace shows that the code gets around the loop() fast enough to retrigger on the same button push, which is part of the lesson in the column

    A midrange timebase:

    Contact Bounce - Matsuhita - NO 3
    Contact Bounce – Matsuhita – NO 3

    You could surely get a few random numbers out of that noise, although the first few bounces seem surprisingly consistent.

  • Random LED Dots: Entropy Library for Moah Speed with Less Gimcrackery

    A discussion over the Squidwrench Operating Table about injecting entropy into VMs before / during their boot sequence reminded me that I wanted to try the Entropy library with my 8×8 RGB LED matrix:

    8x8 RGB LED Matrix - board overview
    8×8 RGB LED Matrix – board overview

    The original version trundled along with random numbers produced by timing Geiger counter ticks. The second version, digitizing the amplified noise from a reverse-biased PN junction, ran much faster.

    What’s new & different: the Entropy library measures the jitter between the ATmega328 watchdog timer’s RC oscillator and the ceramic resonator (on Pro Mini boards) driving the CPU. It cranks out four bytes of uncorrelated bits every half-second, which isn’t quite fast enough for a sparkly display, but re-seeding the Arduino PRNG whenever enough entropy arrives works well enough.

    One could, of course, re-seed the PRNG with Geiger bits or junction noise to the same effect. The key advantage of the Entropy library: no external hardware required. The downside: no external hardware required, so, minus those techie transistors / resistors / op amps, it will look like Just Another Arduino Project.

    Reverse-bias noise amplifier - detail
    Reverse-bias noise amplifier – detail

    Le sigh.

    In any event, the Entropy library has excellent documentation and works perfectly.

    The Arduino PRNG can produce results fast enough for wonderfully twinkly output that’s visually indistinguishable from the “true” random numbers from the Geiger counter or PN junction. I dialed it back to one update every 5 ms, because letting it free-run turned the display into an unattractive blur.

    The top trace shows the update actually happens every 6 ms:

    Entropy TRNG - LED update vs refresh
    Entropy TRNG – LED update vs refresh

    The lower trace shows that each matrix row refresh takes about a millisecond. Refreshes occur on every main loop iteration and interfere with the update, not that that makes any difference. Should it matter, subtract one from the update period and it’ll be all good.

    The Arduino source code as a GitHub Gist:

    // Random LED Dots
    // Based on Entropy library using watchdog timer jitter
    // https://sites.google.com/site/astudyofentropy/project-definition/timer-jitter-entropy-sources/entropy-library
    // Ed Nisley – KE4ANU – August 2016
    #include <Entropy.h>
    //———-
    // Pin assignments
    const byte PIN_HEARTBEAT = 8; // DO – heartbeat LED
    const byte PIN_SYNC = A3; // DO – scope sync
    const byte PIN_LATCH = 4; // DO – shift register latch clock
    const byte PIN_DIMMING = 9; // AO – LED dimming control
    // These are *hardware* SPI pins
    const byte PIN_MOSI = 11; // DO – data to shift reg
    const byte PIN_MISO = 12; // DI – data from shift reg (unused)
    const byte PIN_SCK = 13; // DO – shift clock to shift reg (also Arduino LED)
    const byte PIN_SS = 10; // DO – -slave select (must be positive for SPI output)
    //———-
    // Constants
    #define UPDATE_MS 5
    //———-
    // Globals
    // LED selects are high-active bits and low-active signals: flipped in UpdateLEDs()
    // *exactly* one row select must be active in each element
    typedef struct {
    const byte Row;
    byte ColR;
    byte ColG;
    byte ColB;
    } LED_BYTES;
    // altering the number of rows & columns will require substantial code changes…
    #define NUMROWS 8
    #define NUMCOLS 8
    LED_BYTES LEDs[NUMROWS] = {
    {0x80,0,0,0},
    {0x40,0,0,0},
    {0x20,0,0,0},
    {0x10,0,0,0},
    {0x08,0,0,0},
    {0x04,0,0,0},
    {0x02,0,0,0},
    {0x01,0,0,0},
    };
    byte RowIndex;
    #define LEDS_ON 0
    #define LEDS_OFF 255
    unsigned long MillisNow;
    unsigned long MillisThen;
    //– Helper routine for printf()
    int s_putc(char c, FILE *t) {
    Serial.write(c);
    }
    //– Useful stuff
    // Free RAM space monitor
    // From http://playground.arduino.cc/Code/AvailableMemory
    uint8_t * heapptr, * stackptr;
    void check_mem() {
    stackptr = (uint8_t *)malloc(4); // use stackptr temporarily
    heapptr = stackptr; // save value of heap pointer
    free(stackptr); // free up the memory again (sets stackptr to 0)
    stackptr = (uint8_t *)(SP); // save value of stack pointer
    }
    void TogglePin(char bitpin) {
    digitalWrite(bitpin,!digitalRead(bitpin)); // toggle the bit based on previous output
    }
    void PulsePin(char bitpin) {
    TogglePin(bitpin);
    TogglePin(bitpin);
    }
    //———
    //– SPI utilities
    void EnableSPI(void) {
    digitalWrite(PIN_SS,HIGH); // make sure this is high!
    SPCR |= 1 << SPE;
    }
    void DisableSPI(void) {
    SPCR &= ~(1 << SPE);
    }
    void WaitSPIF(void) {
    while (! (SPSR & (1 << SPIF))) {
    // TogglePin(PIN_HEARTBEAT);
    continue;
    }
    }
    byte SendRecSPI(byte Dbyte) { // send one byte, get another in exchange
    SPDR = Dbyte;
    WaitSPIF();
    return SPDR; // SPIF will be cleared
    }
    void UpdateLEDs(byte i) {
    SendRecSPI(~LEDs[i].ColB); // low-active outputs
    SendRecSPI(~LEDs[i].ColG);
    SendRecSPI(~LEDs[i].ColR);
    SendRecSPI(~LEDs[i].Row);
    analogWrite(PIN_DIMMING,LEDS_OFF); // turn off LED to quench current
    PulsePin(PIN_LATCH); // make new shift reg contents visible
    analogWrite(PIN_DIMMING,LEDS_ON);
    }
    //—————
    // Set LED from integer
    // On average, this leaves the LED unchanged for 1/8 of the calls…
    void SetLED(unsigned long Value) {
    byte Row = Value & 0x07;
    byte Col = (Value >> 3) & 0x07;
    byte Color = (Value >> 6) & 0x07;
    byte BitMask = (0x80 >> Col);
    // printf("%u %u %u %u\r\n",Row,Col,Color,BitMask);
    LEDs[Row].ColR &= ~BitMask;
    LEDs[Row].ColR |= (Color & 0x04) ? BitMask : 0;
    LEDs[Row].ColG &= ~BitMask;
    LEDs[Row].ColG |= (Color & 0x02) ? BitMask : 0;
    LEDs[Row].ColB &= ~BitMask;
    LEDs[Row].ColB |= (Color & 0x01) ? BitMask : 0;
    }
    //——————
    // Set things up
    void setup() {
    pinMode(PIN_HEARTBEAT,OUTPUT);
    digitalWrite(PIN_HEARTBEAT,HIGH); // show we arrived
    pinMode(PIN_SYNC,OUTPUT);
    digitalWrite(PIN_SYNC,LOW);
    pinMode(PIN_MOSI,OUTPUT); // SPI-as-output is not strictly necessary
    digitalWrite(PIN_MOSI,LOW);
    pinMode(PIN_SCK,OUTPUT);
    digitalWrite(PIN_SCK,LOW);
    pinMode(PIN_SS,OUTPUT);
    digitalWrite(PIN_SS,HIGH); // OUTPUT + HIGH is required to make SPI output work
    pinMode(PIN_LATCH,OUTPUT);
    digitalWrite(PIN_LATCH,LOW);
    Serial.begin(57600);
    fdevopen(&s_putc,0); // set up serial output for printf()
    printf("Random LED Dots – Watchdog Entropy\r\nEd Nisley – KE4ZNU – August 2016\r\n");
    Entropy.initialize(); // start up entropy collector
    //– Set up SPI hardware
    SPCR = B01110001; // Auto SPI: no int, enable, LSB first, master, + edge, leading, f/16
    SPSR = B00000000; // not double data rate
    EnableSPI(); // turn on the SPI hardware
    SendRecSPI(0); // set valid data in shift registers: select Row 0, all LEDs off
    //– Dimming pin must use fast PWM to avoid beat flicker with LED refresh rate
    // Timer 1: PWM 9 PWM 10
    analogWrite(PIN_DIMMING,LEDS_OFF); // disable column drive (hardware pulled it low before startup)
    TCCR1A = B10000001; // Mode 5 = fast 8-bit PWM with TOP=FF
    TCCR1B = B00001001; // … WGM, 1:1 clock scale -> 64 kHz
    //– lamp test: send a white flash through all LEDs
    printf("Lamp test begins: white flash each LED…");
    digitalWrite(PIN_HEARTBEAT,LOW); // turn off while panel blinks
    analogWrite(PIN_DIMMING,LEDS_ON); // enable column drive
    for (byte i=0; i<NUMROWS; i++) {
    for (byte j=0; j<NUMCOLS; j++) {
    LEDs[i].ColR = LEDs[i].ColG = LEDs[i].ColB = 0x80 >> j;
    for (byte k=0; k<NUMROWS; k++) {
    UpdateLEDs(k);
    delay(25);
    }
    LEDs[i].ColR = LEDs[i].ColG = LEDs[i].ColB = 0;
    }
    }
    UpdateLEDs(NUMROWS-1); // clear the last LED
    printf(" done!\r\n");
    //– Preload LEDs with random values
    digitalWrite(PIN_HEARTBEAT,LOW);
    uint32_t rn = Entropy.random();
    printf("Preloading LED array with seed: %08lx\r\n",rn);
    randomSeed(rn);
    for (byte Row=0; Row<NUMROWS; Row++) {
    for (byte Col=0; Col<NUMCOLS; Col++) { // Col runs backwards, but we don't care
    LEDs[Row].ColR |= random(2) << Col; // random(2) returns 0 or 1
    LEDs[Row].ColG |= random(2) << Col;
    LEDs[Row].ColB |= random(2) << Col;
    }
    UpdateLEDs(Row);
    }
    check_mem();
    printf("SP: %u HP: %u Free RAM: %u\r\n",stackptr,heapptr,stackptr – heapptr);
    printf("Running…\r\n");
    MillisThen = millis();
    }
    //——————
    // Run the test loop
    void loop() {
    unsigned long Hash;
    uint32_t rn;
    MillisNow = millis();
    // Re-seed the generator whenever we get enough entropy
    if (Entropy.available()) {
    digitalWrite(PIN_HEARTBEAT,HIGH);
    rn = Entropy.random();
    // printf("Random: %08lx ",rn);
    randomSeed(rn);
    digitalWrite(PIN_HEARTBEAT,LOW);
    }
    // If it's time for a change, whack a random LED
    if ((MillisNow – MillisThen) > UPDATE_MS) {
    MillisThen = MillisNow;
    SetLED(random());
    }
    // Refresh LED array to maintain the illusion of constant light
    UpdateLEDs(RowIndex++);
    if (RowIndex >= NUMROWS) {
    RowIndex = 0;
    PulsePin(PIN_SYNC);
    }
    }
    view raw TimerDots.ino hosted with ❤ by GitHub
  • Kenmore Model 158: Needle Lights, Now With Moah LEDs

    The first pass at retrofitting SMD LEDs to light the needle area in Mary’s Model 158 sewing machine worked well enough:

    Kenmore 158 Needle Light - heatsink
    Kenmore 158 Needle Light – heatsink

    However, she wanted more light on the right side of the needle, so now she has it:

    Needle LEDs - front
    Needle LEDs – front

    That’s without any LEDs along the front and back of the arm, hence the dark pool beyond the sewing machine’s base.

    Those are the same 5050 warm white LEDs I used on the other side:

    Needle LEDs - lower right
    Needle LEDs – lower right

    Seen without the glare:

    Needle LEDs - bottom
    Needle LEDs – bottom

    They’re mounted on a 32 mil brass strip from the shimstock stash, carefully hand-bent and twisted to match the curvature of the arm, and held in place with JB Kwik steel-filled epoxy for good heat conduction to the aluminum arm. One can argue with the epoxy oozing out from under the brass, but it’s invisible from above.

    No construction photos, alas, because I made this in a white-hot frenzy one afternoon and managed to not take any pix during the entire session. Call it working in the flow, OK?

    All four SMD LEDs sit in epoxy blobs that isolate them from the brass strip, with 26 AWG solid wire “bus bars” soldered to the top of their terminals and a length of that lovely PTFE-insulated miniature coax leading off into the endcap. More epoxy encloses all the wiring & connections to provide a surprisingly smooth surface that shouldn’t snag the fabric.

    The power supply uses an 18 W 120 VAC to 12 VDC brick intended for small LED installations:

    Needle LEDs power supply - exterior
    Needle LEDs power supply – exterior

    The AC comes from the same zip cord that formerly supplied the original 15 W incandescent bulb in the endcap, so the new lights behave the same way: push the power button to turn on the machine and the LEDs pop on just like they should. I put quick-disconnect terminals in the AC line to make it removable, although those need some sort of insulated plug to cover the exposed blades inside their housing.

    Inside the black box, a small boost supply steps the voltage up to just under the nominal operating level of 21 VDC:

    Needle LEDs power supply - interior
    Needle LEDs power supply – interior

    You can just see the adjusting screw hole in front of the AC brick in the overall view.

    The DC output exits in the middle of the far side, through a coax jack epoxied to the base.

    As before, all six LEDs run in parallel at (for now) 18.5 VDC and maybe 50 mA each, for a total of 300 mA, and seem fearsomely bright even at that. We can now tune for best light as needed.

    This is a major major major improvement over the previous tangle of wires stuck on the outside of the machine, with all the wiring internal to the arm and the power supply out of sight under the sewing table.

    After an hour, the arm above the four LEDs runs 13 °C above ambient and the endcap over the two LED heatsink is 6 °C over ambient. The AC supply runs at 104 °C and its plastic case offers no provision for heatsinking. All in all, things are warm and not hazardous.

    I haven’t retrofit this machine with LED strips along the front & back of the arm, as those may not be needed with the intense needle lighting; the NisLite desk lamp may suffice for area illumination.

  • Vacuum Tube LEDs: 500 W Frosted Incandescent Bulb

    This turned out surprisingly well:

    500 W Incandescent - backlit dark
    500 W Incandescent – backlit dark

    In the harsh light of the Electronics Workbench, you can see there’s less than meets the eye: a single knockoff Neopixel taped to the back side of the bulb just below the equator and a knockoff Arduino Pro Mini taped to the Mogul lamp socket:

    500 W Incandescent - backlit light
    500 W Incandescent – backlit light

    The electrical box serves as a base and the cord doesn’t do anything in this incarnation.

    The 5050 SMD LED package (inside an ugly 3D printed plate cap) looks enough like a point source to shadow the filament & support structure against the frosted bulb. The blurry upper part of the filament is closer to the LED, which isn’t really a point source and must fight its way through the frosting.

    The Pro Mini runs the same firmware as the Bowl o’ Fire floodlamp, of course, dialed back for slow fades.

    It lights up the room something wonderful …

  • FG085 Function Generator

    The topic of function generators came up at Squidwrench a while ago (Sophi was tinkering with LCD shutters) and I finally picked up one of those JYE Tech FG085 DDS function generators to see how they work:

    FG085 Fn Gen - in case
    FG085 Fn Gen – in case

    Short answer: adequate, if you’re not too fussy.

    The board arrived with a bizarre solder defect. It seems a solder stalk yanked one terminal off a ceramic SMD caps:

    FG085 - Solder stalk - C26
    FG085 – Solder stalk – C26

    The schematic and adjacent parts suggested the victim was a 10 uF cap, so I replaced it with one from my stash that worked fine.

    However, after soldering enough of the switches to do something useful, the board wouldn’t power up. With a bit of poking around, I discovered the power jack had +15 V from the wall wart, but the center terminals on the DPDT power switch that should have been connected to the jack showed maybe 0.3 V. Jumpering around the failed via and a short trace on the bottom surface let the board power up correctly:

    FG085 - Jumpered power trace
    FG085 – Jumpered power trace

    If you’re building one of these, solder one pin of each switch, push all the switch caps in place, shove the faceplate over all of them, tape it to the PCB, make sure all the switches are push-able, then solder the remainder of the switch pins. If you do them one by one, you’re certain to end up with a few mis-aligned switches that will either prevent the faceplate from sliding over them or wedge firmly against the side of their assigned hole. Just sayin’.

    It lives in a case from Thingiverse:

    FG085enclosure - 1268379
    FG085enclosure – 1268379

    I tweaked the dimensions slightly to fit the (slightly larger, possibly new, maybe tolerance-eased) front panel, but the bottom mounting screw hole spacing depends on the front panel size, not a specific set of dimensions, leading me to relocate those holes by abrasive adjustment. I didn’t bother with the lid (which doesn’t clear the BNC jack anyway) or the printed plastic feet (having a supply of silicone rubber feet).

    The fancy vent gridwork along the sides printed surprisingly well, even in PETG. I’d have gone with larger slots, although I doubt the thing really needs vents in the first place.

    The DDS sine wave output is rough, to say the least:

    FG085 Fn Gen - 60 kHz sine
    FG085 Fn Gen – 60 kHz sine

    The spectrum shows oodles of harmonic content:

    FG085 Fn Gen - 60 kHz sine - spectrum
    FG085 Fn Gen – 60 kHz sine – spectrum

    A closer look:

    FG085 Fn Gen - 60 kHz sine - spectrum - detail
    FG085 Fn Gen – 60 kHz sine – spectrum – detail

    Stepping back a bit shows harmonics of (and around) the 2.5 MHz DDS sampling frequency:

    FG085 Fn Gen - 60 kHz sine - spectrum - 10 MHz
    FG085 Fn Gen – 60 kHz sine – spectrum – 10 MHz

    For comparison, my old Fordham FG-801 analog function generator has nice smooth harmonics:

    FG-801 Fn Gen - 60 kHz sine - spectrum
    FG-801 Fn Gen – 60 kHz sine – spectrum

    Closer in:

    FG-801 Fn Gen - 60 kHz sine - spectrum - detail
    FG-801 Fn Gen – 60 kHz sine – spectrum – detail

    Of course, that crusty old analog dial doesn’t provide nearly the set-ability of a nice digital display.

     

  • LF Loop Antenna: GPS Frequency Check

    I stuck some old 12 V 7 A·h batteries in my homebrew power supply for the HP 3801A GPS Time / Frequency Standard, fired it up, put the antenna where it could see a good chunk of the sky, gave it a day to warm up / settle out, and it’s perfectly happy:

    ------------------------------- Receiver Status -------------------------------
    
    SYNCHRONIZATION ............................................. [ Outputs Valid ]
    SmartClock Mode ___________________________   Reference Outputs _______________
    >> Locked to GPS                              TFOM     3             FFOM     0
       Recovery                                   1PPS TI -38.3 ns relative to GPS
       Holdover                                   HOLD THR 1.000 us
       Power-up                                   Holdover Uncertainty ____________
                                                  Predict  366.2 us/initial 24 hrs
    
    ACQUISITION ............................................ [ GPS 1PPS CLK Valid ]
    Satellite Status __________________________   Time _____ +1 leap second pending
    Tracking: 4        Not Tracking: 6            UTC      18:22:19     22 Jul 2016
    PRN  El  Az   SS   PRN  El  Az                1PPS CLK Synchronized to UTC
      3  34 104   48   * 1  36  48                ANT DLY  0 ns
     17  62 308  103     6  27 220                Position ________________________
     19  39 281   50    11  21  58                MODE     Hold
     28  80 133   64   *22  Acq .
                        24  12 319                LAT      N  41:39:32.328
                        30  15 191                LON      W  73:52:26.733
    ELEV MASK 10 deg   *attempting to track       HGT               +82.87 m  (MSL)
    HEALTH MONITOR ......................................................... [ OK ]
    Self Test: OK    Int Pwr: OK   Oven Pwr: OK   OCXO: OK   EFC: OK   GPS Rcv: OK
    scpi >
    

    The FFOM 0 entry says the Frequency Figure Of Merit is “within specifications” of 10-9, averaged over one day. That means the actual frequency should be within 0.010 Hz of 10 MHz.

    Feeding the 10 MHz frequency reference into the (equally warmed up) HP 8591E spectrum analyzer and selecting an absurdly narrow span produces a comforting sight:

    HP Z2801A GPS Receiver - 10 MHz ref - HP 8591E
    HP Z2801A GPS Receiver – 10 MHz ref – HP 8591E

    Given the horizontal resolution, that’s dead on 10 MHz.

    So, yeah, that signal at 57-ish kHz really isn’t at 60.000 kHz:

    Loop - 40T 1nF - spectrum
    Loop – 40T 1nF – spectrum

    Which is good to know …

  • External Li-Ion Pack: More Sawing

    Two of the external Li-Ion battery packs I’m using with the bike radios seemed to fail quickly after being charged, so I sawed them open to check the state of the cells. This time I used the fine-tooth cutoff blades, rather than a coarse slitting saw:

    Li-Ion pack - sawing case
    Li-Ion pack – sawing case

    As before, a 2 mm depth-of-cut, done 0.25 mm per pass after the first millimeter, seems about right. I didn’t saw the front of the case near the jack, which proved to be a mistake; the interlocked case halves need cutting.

    No cell trouble found, which leads me to suspect an intermittent short in the battery-to-radio cable that trips the battery protection circuit. The spare cables went into hiding during the shop cleanout, so I can’t swap in a known-good cable just yet; of course, the existing cable behaves perfectly on the bench. The suspect cable is now on my bike and, if the problem follows the cable, further surgery will be in order.

    For the record, the insides look like this:

    Li-Ion pack - interior
    Li-Ion pack – interior

    The cell label seems to show a 2004 date code:

    Li-Ion pack - cell label
    Li-Ion pack – cell label

    Given that I got them on closeout in early 2010, it definitely isn’t 2014.

    Unlike some of the other cheap batteries around here, they’ve been spectacularly successful!