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

  • Floor Lamp Rebasing

    Floor Lamp Rebasing

    The torchiere floor lamp in the sewing room suffered a catastrophic failure:

    Floor lamp - failed plastic base shell
    Floor lamp – failed plastic base shell

    Contrary to what you might think from seeing the shattered plastic base, we didn’t use the lamp as a club or battering ram. Apparently the designer expected the thin plastic surrounding the hole to withstand all the torque produced by the long pole against the cheap concrete / mortar / grout / whatever lump in the base. As we can recall, this lamp came to us from either a yard sale or a roadside debris harvest, so I suppose the hardware outlasted any reasonable expectation.

    The Basement Laboratory Warehouse disgorged the pole and base from a similar lamp, albeit sporting black paint and a smaller rod connecting its pole to its somewhat larger weight. Not being too fussy about decor, I embiggened the hole in the black base to fit the white lamp’s threaded rod:

    Floor lamp - enlarging replacement base
    Floor lamp – enlarging replacement base

    The dust on the base shows why you shouldn’t stand motionless in the Basement Laboratory for very long.

    The alert reader will have noted the cord passing through a strain relief grommet in the white base. Rather than dismantle the entire lamp, I just cut the cord, ran it through the new base weight, reinstalled the washer + nut, then crimped on a pair of solderless connectors:

    Floor lamp - cord splice
    Floor lamp – cord splice

    The new base doesn’t offer much in the way of attachment points, so I added a cable tie to keep the strain off the connectors:

    Floor lamp - cord strain relief
    Floor lamp – cord strain relief

    A strip of genuine 3M duct tape with double-thick adhesive now traps the cord inside that small channel and, given that the lamps spends most of its time standing quietly in a corner, the cord should be fine for long enough.

  • KeyboardIO Atreus: RGB LED Firmware

    KeyboardIO Atreus: RGB LED Firmware

    Having wired a WS2812 RGB LED into my KeyboardIO Atreus, lighting it up requires some QMK firmware configuration. It’s easiest to set up a “new” keymap based on the QMK Atreus files, as described in the QMK startup doc:

    qmk new-keymap -kb keyboardio/atreus -km ednisley

    Obviously, you’ll pick a different keymap name than I did. All the files mentioned below will reside in the new subdirectory, which starts out with only a keymap.c file copied from the default layout.

    The rules.mk file enables RGB Lighting, as well as Auto Shift and Tap Dance:

    AUTO_SHIFT_ENABLE = yes			# allow automagic shifting
    TAP_DANCE_ENABLE = yes			# allow multi-tap keys
    
    RGBLIGHT_ENABLE = yes			# addressable LEDs
    

    If you had different hardware, you could specify the driver with a WS2812_DRIVER option.

    QMK can also control single-color LEDs with PWM (a.k.a. backlighting), and per-key RGB LEDs (a.k.a. RGB Matrix). These functions, their configuration / controls / data, and their documentation overlap and intermingle to the extent that I spent most of my time figuring out what not to include.

    Some configuration happens in the config.h file:

    #define RGB_DI_PIN B2
    #define RGBLED_NUM 1
    
    // https://github.com/qmk/qmk_firmware/blob/master/docs/ws2812_driver.md
    //#define WS2812_TRST_US 280
    //#define WS2812_BYTE_ORDER WS2812_BYTE_ORDER_GRB
    
    #define RGBLIGHT_LAYERS
    #define RGBLIGHT_EFFECT_RGB_TEST
    #define RGBLIGHT_LIMIT_VAL 63
    
    #define NO_DEBUG
    #define NO_PRINT
    

    The first two lines describe a single WS2812 RGB LED wired to pin B2 (a.k.a. MOSI) of the Atmel 32U4 microcontroller. The default Reset duration and Byte Order values work for the LED I used

    Protip: swapping the order from GRB to RGB is a quick way to discover if the firmware actually writes to the LED, even before you get anything else working: it’ll be red with the proper setting and green with the wrong one.

    Dialing the maximum intensity down works well with a bright LED shining directly at your face from a foot away.

    Turning on RGBLIGHT_LAYERS is what makes this whole thing happen. The RGBLIGHT_EFFECT_RGB_TEST option enables a simple test animation at the cost of a few hundred bytes of code space; remove that line after everything works.

    The last two lines remove the debugging facilities; as always with microcontroller projects, there’s enough room for either your code or the debugger required to get it running, but not both.

    With those files set up, the keymap.c file does the heavy lifting:

    // Modified from the KeyboardIO layout
    // Ed Nisley - KE4ZNU
    
    #include QMK_KEYBOARD_H
    
    enum layer_names {
        _BASE,
        _SHIFTS,
        _FUNCS,
        _NLAYERS
    };
    
    // Tap Dance
    
    enum {
        TD_SPC_ENT,
    };
    
    qk_tap_dance_action_t tap_dance_actions[] = {
        [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT),
    };
    
    
    // Layer lighting
    
    // Undefine this to enable simple test mode
    // Also put #define RGBLIGHT_EFFECT_RGB_TEST in config.h
    
    #define LED_LL
    
    #ifdef LED_LL
    
    const rgblight_segment_t PROGMEM ll_0[] = RGBLIGHT_LAYER_SEGMENTS( {0,1,HSV_WHITE} );
    const rgblight_segment_t PROGMEM ll_1[] = RGBLIGHT_LAYER_SEGMENTS( {0,1,HSV_MAGENTA} );
    const rgblight_segment_t PROGMEM ll_2[] = RGBLIGHT_LAYER_SEGMENTS( {0,1,HSV_CYAN} );
    const rgblight_segment_t PROGMEM ll_3[] = RGBLIGHT_LAYER_SEGMENTS( {0,1,HSV_BLUE} );
    const rgblight_segment_t PROGMEM ll_4[] = RGBLIGHT_LAYER_SEGMENTS( {0,1,HSV_GREEN} );
    const rgblight_segment_t PROGMEM ll_5[] = RGBLIGHT_LAYER_SEGMENTS( {0,1,HSV_RED} );
    const rgblight_segment_t PROGMEM ll_6[] = RGBLIGHT_LAYER_SEGMENTS( {0,1,HSV_YELLOW} );
    
    const rgblight_segment_t* const PROGMEM ll_layers[] = RGBLIGHT_LAYERS_LIST(
        ll_0,ll_1,ll_2,ll_3,ll_4,ll_5,ll_6
    );
    
    #endif
    
    void keyboard_post_init_user(void) {
    
    #ifdef LED_LL
        rgblight_layers = ll_layers;
        rgblight_set_layer_state(0, 1);
    #else
        rgblight_enable_noeeprom();
        rgblight_mode_noeeprom(RGBLIGHT_MODE_RGB_TEST);
    //    rgblight_mode_noeeprom(RGBLIGHT_MODE_BREATHING + 3);
    #endif
    
    }
    
    
    #ifdef LED_LL
    
    layer_state_t layer_state_set_user(layer_state_t state) {
        for (uint8_t i=0 ; i < _NLAYERS; i++)
            rgblight_set_layer_state(i, layer_state_cmp(state, i));
    
        return state;
    }
    #endif
    
    
    // Key maps
    
    const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
      [_BASE] = LAYOUT(                             // base layer for typing
        KC_Q,    KC_W,    KC_E,    KC_R,    KC_T,                      KC_Y,    KC_U,    KC_I,    KC_O,    KC_P    ,
        KC_A,    KC_S,    KC_D,    KC_F,    KC_G,                      KC_H,    KC_J,    KC_K,    KC_L,    KC_SCLN ,
        KC_Z,    KC_X,    KC_C,    KC_V,    KC_B,    KC_GRV,  KC_LALT, KC_N,    KC_M,    KC_COMM, KC_DOT,  KC_SLSH ,
        LT(_FUNCS,KC_ESC), KC_TAB, KC_LGUI,  KC_BSPC, KC_LSFT,  KC_LCTL, KC_ENT , TD(TD_SPC_ENT),  MO(_SHIFTS), KC_MINS, KC_QUOT, KC_BSLS),
    
      [_SHIFTS] = LAYOUT(                           // shifted chars and numpad
        KC_EXLM, KC_AT,   KC_UP,   KC_DLR,  KC_PERC,                  KC_PGUP, KC_7,    KC_8,   KC_9, KC_HOME,
        KC_LPRN, KC_LEFT, KC_DOWN, KC_RGHT, KC_RPRN,                  KC_PGDN, KC_4,    KC_5,   KC_6, KC_END,
        KC_LBRC, KC_RBRC, KC_HASH, KC_LCBR, KC_RCBR, KC_CIRC, KC_AMPR,KC_ASTR, KC_1,    KC_2,   KC_3, KC_PLUS,
        KC_NO  , KC_INS,  KC_LGUI, KC_DEL , KC_BSPC, KC_LCTL, KC_LALT,KC_SPC,  KC_TRNS, KC_DOT, KC_0, KC_EQL ),
    
      [_FUNCS] = LAYOUT(                            // function keys
        KC_INS,  KC_HOME, KC_UP,   KC_END,  KC_PGUP,                   KC_UP,   KC_F7,   KC_F8,   KC_F9,   KC_F10  ,
        KC_DEL,  KC_LEFT, KC_DOWN, KC_RGHT, KC_PGDN,                   KC_DOWN, KC_F4,   KC_F5,   KC_F6,   KC_F11  ,
        KC_NO,   KC_VOLU, KC_NO,   KC_NO,   RESET,   _______, _______, KC_NO,   KC_F1,   KC_F2,   KC_F3,   KC_F12  ,
        KC_NO,   KC_VOLD, KC_LGUI, KC_LSFT, KC_BSPC, KC_LCTL, KC_LALT, KC_SPC,  TO(_BASE), KC_PSCR, KC_SLCK, KC_PAUS )
    };
    

    Undefine LED_LL to enable the test mode, compile, flash, and the LED should cycle red / green / blue forever; you also need the RGB_TEST option in the config.h file.

    Define LED_LL and layer lighting should then Just Work™, with the LED glowing:

    • White for the basic layer with all the letters
    • Magenta with the Fun key pressed
    • Cyan with the Esc key pressed

    The key map code defines colors for layers that don’t yet exist, but it should get you started.

    For convenience, I wadded all three QMK files into a GitHub Gist.

    The LED is kinda subtle:

    Atreus keyboard - LED installed
    Atreus keyboard – LED installed

    As you might expect, figuring all that out took much longer than for you to read about it, but now I have a chance of remembering what I did.

  • KeyboardIO Atreus: RGB LED Installation

    KeyboardIO Atreus: RGB LED Installation

    Having scouted out the territory inside the KeyboardIO Atreus, adding an LED requires taking it completely apart to drill a hole in the aluminum faceplate:

    Atreus keyboard - panel drilling
    Atreus keyboard – panel drilling

    Reattaching the plate to the PCB with only three screws allows marking the hole position on the PCB, which is much easier than pretending to derive the position from first principles:

    Atreus keyboard - LED marking
    Atreus keyboard – LED marking

    Despite appearances, I traced the hole with a mechanical pencil: black graphite turns shiny silvery gray against matte black soldermask. Also, the PCB trace is off-center, not the hole.

    Overlay the neighborhood with Kapton tape to protect the PCB from what comes next:

    Atreus keyboard - Kapton tape

    Snip a WS2812 RGB LED from a strip, stick it in place with eyeballometric alignment over the target, and wire it up:

    Atreus keyboard - LED wiring
    Atreus keyboard – LED wiring

    Despite the terrible reliability of WS2812 RGB LEDs mounted on PCB carriers, a different set on a meter of high-density flex tape have worked reasonably well when not thermally stressed, so I’ll assume this one arrived in good order.

    Aligning the LED directly under the hole required a few iterations:

    Atreus keyboard - LED positioning
    Atreus keyboard – LED positioning

    The iridescent green patch is a diffraction pattern from the controller chip’s internal circuitry.

    The data comes from MOSI, otherwise known as B2, down in the lower left corner:

    Atmel 32U4 - JTAG pins
    Atmel 32U4 – JTAG pins

    Actually lighting the LED now becomes a simple matter of software QMK firmware.

  • Enover Outlet Timer: Over-powered Zener Diode

    Enover Outlet Timer: Over-powered Zener Diode

    This being the season of lights, I deployed some outlet timers to turn them on at dusk and off at bedtime. The timers spend much of the rest of their lives plugged into outlets in the Basement Laboratory to keep their internal NiMH backup batteries charged, although they’re not controlling anything:

    Enover outlet timer - overview
    Enover outlet timer – overview

    This one is labeled ENOVER, but it’s essentially identical to all the others sporting random alphabetic names; I have a few more labeled UKOKE in the same plastic case. The current crop uses a different case and has one fewer button, but don’t expect any real difference.

    One of the timers had a blank display and didn’t respond to button pushes or a pin punch poked in the RESET hole, so I dismantled it to see what was inside.

    Both the hot and neutral terminals had stray wire strands:

    Enover outlet timer - stray wire strand
    Enover outlet timer – stray wire strand

    The power board had the usual missing components, suggesting it had been cheapnified after passing whatever regulatory inspection it might have endured to get a CE mark on its dataplate:

    Enover outlet timer - power board - overview
    Enover outlet timer – power board – overview

    The alert reader may have already noticed the mmmmm smoking gun:

    Enover outlet timer - scorched diode
    Enover outlet timer – scorched diode

    Incredibly, Z1 has a part number wrapped around it! A quick lookup shows a 1N4749A is a 24 V 1 W Zener diode, neatly matching the 24 V relay. The datasheet gives a 10.5 mA test current and a 38 mA maximum regulator current, with a caveat: “Valid provided that electrodes at a distance of 10mm from case are kept at ambient temperature”

    The relay datasheet says 8.3 mA nominal coil current, a mere 200 mW, which is much easier to dissipate in wire wrapped around a steel core than in a little diode.

    Evidently the poor diode ran rather hot before becoming a dead short, because a phenolic PCB (definitely not at ambient temperature) ought not discolor like that.

    Indeed, measuring Z1 in another, still functional, Enover timer showed 25 V and a similarly discolored patch around Z1, suggesting the circuit design requires a bit more disspation from the diode than it can comfortably deliver.

    I replaced it with a 1N970B from the Basement Laboratory Warehouse, rated for only 0.5 W in a seemingly identical case, buttoned the whole thing up, and left it in the middle of the concrete basement floor overnight. It wasn’t smoking and continued working in the morning, so I defined things to be no worse than before and declared victory.

    Should when the next one fails the same way, I’ll epoxy a small heatsink to that poor diode and its leads to reduce its overall temperature.

    For future reference, the underside of the PCB shows a distinct lack of post-soldering flux cleanup:

    Enover outlet timer - power board - solder side
    Enover outlet timer – power board – solder side

    I swabbed it with denatured alcohol, although doing so certainly didn’t make any change to its behavior.

    Memo to Self: no-clean flux is a thing.

    It’s worth noting no other components show signs of overheating, despite the diode becoming a short circuit, so R1 (a big power resistor) is most likely the shunt regulator’s dropping resistor and can survive the additional power.

    Should the diode fail open, the rest of the circuitry will be toast.

  • Astable Multivibrator: Amber LED

    Astable Multivibrator: Amber LED

    Adding an amber LED to the collection:

    Astable AA - Amber - overview
    Astable AA – Amber – overview

    Because a yellow / amber LED runs at a lower voltage than blue and green LEDs, it sits atop an astable multivibrator, rather than a discrete LM3909. The battery holder has a pair of carbon-zinc “Extra-Heavy Duty” AAA cells, so corrosion and leakage pose a foreseeable hazard.

    The voltage across the 100 Ω LED ballast indicates a 9 mA peak LED current, which is somewhat dim in ordinary room light:

    Astable AA - Amber - LED current 100 ohm
    Astable AA – Amber – LED current 100 ohm

    The corresponding LED voltage says the LED runs at 2.1 V for that much current:

    Astable AA - Amber - LED V
    Astable AA – Amber – LED V

    Something around 39 Ω should make it more visible.

  • Monthly Science: Batmax NP-BX1 Status

    Monthly Science: Batmax NP-BX1 Status

    After powering my Sony HDR-AS30V helmet camera for nearly all of this year’s riding, the Batmax NP-BX1 lithium batteries still have roughly 90% of their original capacity:

    Batmax NP-BX1 - 2020-11
    Batmax NP-BX1 – 2020-11

    Those are hot off the Official Batmax charger, which appears identical to other randomly named chargers available on Amazon.

    They’re holding up much better after a riding season than the DOT-01 batteries I used two years ago:

    Sony DOT-01 NP-BX1 - 2019-10-29
    Sony DOT-01 NP-BX1 – 2019-10-29

    Empirically, they power the camera for about 75 minutes, barely enough for our typical rides. I should top off the battery sitting in the camera unused for a few days, although that hasn’t happened yet.

    Of course, the Batmax NP-BX1 batteries I might order early next year for the new riding season have little relation to the ones you see here.

  • Astable Multivibrator: Dressed-up LED Spider

    Astable Multivibrator: Dressed-up LED Spider

    Adding a bit of trim to the bottom of the LED spider makes it look better and helps keep the strut wires in place:

    Astable Multivibrator - Alkaline - Radome trim
    Astable Multivibrator – Alkaline – Radome trim

    It’s obviously impossible to build like that, so it’s split across the middle of the strut:

    Astable Multivibrator - Alkaline - Radome trim
    Astable Multivibrator – Alkaline – Radome trim

    Glue it together with black adhesive and a couple of clamps:

    LED Spider - glue clamping
    LED Spider – glue clamping

    The aluminum fixtures (jigs?) are epoxied around snippets of strut wire aligning the spider parts:

    LED Spider - gluing fixture
    LED Spider – gluing fixture

    Those grossly oversized holes came pre-drilled in an otherwise suitable aluminum rod from the Little Tray o’ Cutoffs. I faced off the ends, chopped the rod in two, recessed the new ends, and declared victory. Might need better ones at some point, but they’ll do for now.

    Next step: wire up an astable with a yellow LED to go with the green and blue boosted LEDs.