Blog Summary: 2020

You can’t make up results like this for a techie kind of blog:

Blog Top Post Summary - 2020-12-31
Blog Top Post Summary – 2020-12-31

Given my demographic cohort, bedbugs suddenly seemed downright friendly.

Overall, this blog had 109 k visitors and 204 k page views. The ratio of 1.8 pages / visitor has been roughly constant for the last few years, so I assume most folks find one more interesting post before wandering off.

My take from the increasing volume of ads WordPress shovels at those of you who (foolishly) aren’t using an ad blocker continues to fall:

Blog Ad Summary - 2020-12-31
Blog Ad Summary – 2020-12-31

The CPM graph scale seems deliberately scrunched, but the value now ticks along at 25¢ / thousand impressions, adding up to perhaps $250 over the full year. Obviously, I’m not in this for the money.

The ratio of five ads per page view remains more or less constant. Because Google continues to neuter Chrome’s ad blocking ability, I highly recommend using Firefox with uBlock Origin.

WordPress gives me no control over which ads they serve, nor where they put ads on the page. By paying WordPress about $50 / year I could turn off all their ads and convert the blog into a dead loss. I’m nearing their 3 GB limit for media files on a “free” blog, so the calculation may change late next year.

Onward, into Year Two …

KeyboardIO Atreus Keymapping

Having a customizable keyboard like the KeyboardIO Atreus means one must customize it. As it turns out, I wanted to use some features of the underlying QMK Kaleidoscope firmware that aren’t exposed by Chrysalis, KeyboardIO’s otherwise competent keymap configuration utility, so what you see below runs on hard mode.

Start by installing QMK, compiling the default Atreus layout, and flashing the keyboard just to confirm all the steps work:

Atreus keyboard - overview
Atreus keyboard – overview

With all that working, add (or create) two lines to the rules.mk file in the keymap directory you’re tweaking:

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

Enabling Auto-Shift lets you generate shifted characters (like Z) by briefly holding down the unshifted key (like z). This requires unlearning an entire lifetime of touch typing practice, but is definitely worthwhile; if a thumb still reaches for the shift key, there’s no harm done. There are, of course, a myriad options, all of which I left unchanged.

Complex passwords suffer, as you must blind-type carefully while tapping each key rapidly.

Enabling Tap Dance lets a key generate one character when tapped and another when double-tapped; you can go crazy with more taps. An enum{} in the keymap.c file generates indexes for the keys and an array holds the action definitions:

enum {
    TD_SPC_ENT,
    TD_BS_DEL,
};

qk_tap_dance_action_t tap_dance_actions[] = {
    [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT),
    [TD_BS_DEL] = ACTION_TAP_DANCE_DOUBLE(KC_BSPC, KC_DEL),
};

Then each key uses a TD() macro in the keymap.c file:

… TD(TD_BS_DEL), … TD(TD_SPC_ENT), …

In contrast, layer shifting uses straightforward built-in macros. The Fun key produces a momentary shift to Layer 1 (known as _RS) when held down:

… MO(_RS), …

The ESC key in the lower left corner emits the expected Escape key code when tapped and switches to Layer 2 (a.k.a. _LW) when held:

LT(_LW,KC_ESC), …

For reference, the current state of the keymap.c file:

// Modified from the KeyboardIO layout
// Ed Nisley - KE4ZNU
// Enable Auto Shift and Tap Dance in rules.mk

#include QMK_KEYBOARD_H

enum layer_names {
    _QW,
    _RS,
    _LW,
};

enum {
    TD_SPC_ENT,
    TD_BS_DEL,
};

qk_tap_dance_action_t tap_dance_actions[] = {
    [TD_SPC_ENT] = ACTION_TAP_DANCE_DOUBLE(KC_SPC, KC_ENT),
    [TD_BS_DEL] = ACTION_TAP_DANCE_DOUBLE(KC_BSPC, KC_DEL),
};

const uint16_t PROGMEM keymaps[][MATRIX_ROWS][MATRIX_COLS] = {
  [_QW] = LAYOUT( /* Qwerty */
    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(_LW,KC_ESC), KC_TAB, KC_LGUI,  TD(TD_BS_DEL), KC_LSFT,  KC_LCTL, KC_ENT , TD(TD_SPC_ENT),  MO(_RS), KC_MINS, KC_QUOT, KC_BSLS),

  [_RS] = LAYOUT( /* [> RAISE <] */
    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 ),

  [_LW] = LAYOUT( /* [> LOWER <] */
    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(_QW), KC_PSCR, KC_SLCK, KC_PAUS )
};

With all that set up, It Just Works and I can contemplate grafting a status LED into the thing.

Sharing the Lane in Red Oaks Mill

We’re in the middle of three southbound lanes on Rt 376 in Red Oaks Mill, turning left into the rightmost lane going down the hill across the bridge, when a car approaches from behind:

Red Oaks Mill Intersection - close pass - approach - 2020-12-24
Red Oaks Mill Intersection – close pass – approach – 2020-12-24

Most drivers seem content to wait behind us until we get into the huge intersection where there’s plenty of room (comparatively speaking) to pass, but not this one:

Red Oaks Mill Intersection - close pass - waiting - 2020-12-24
Red Oaks Mill Intersection – close pass – waiting – 2020-12-24

I warned Mary (one the reasons we have radios on our bikes) about the mirror just behind her shoulder and she verified the minimal clearance:

Red Oaks Mill Intersection - close pass - arms length - 2020-12-24
Red Oaks Mill Intersection – close pass – arms length – 2020-12-24

Prudence dictated we wait until he was clear before moving:

Red Oaks Mill Intersection - close pass - rolling - 2020-12-24
Red Oaks Mill Intersection – close pass – rolling – 2020-12-24

Of course, the signal timing doesn’t let us get all the way through the intersection under the best of conditions, but we make an impressive enough parade to keep oncoming cars from moving before we’re out of their way.

This section of NY Rt 376 is also NY Bike Route 9, which doesn’t explain why NYS DOT pays so little attention to bicycle safety.

Straightening Armature Wire

Although I was blithely unaware when I bought some useful-looking surplus, it turns out 1/16 inch armature wire works really well to seal our homebrew masks around our noses. Mary added a narrow passage along the top edge of her slightly reshaped Fu Mask pattern to retain the wire and I provided 4.5 inch lengths of straightened wire:

Armature wire - stock vs. straightened
Armature wire – stock vs. straightened

The wire comes off the roll in dead-soft condition, so I can straighten (and slightly harden) it by simply rolling each wire with eight fingertips across the battered cutting board. The slightly wavy wire shows its as-cut condition and the three straight ones are ready for their masks.

Although nearly pure aluminum wire doesn’t work-harden quickly, half a year of mask duty definitely takes its toll. This sample came from my biking mask after the edges wore out:

Armature wire - work-hardened
Armature wire – work-hardened

We initially thought using two wires would provide a better fit, but more metal just made adjusting the nose seal more difficult after each washing. The wire has work-hardened enough to make the sharper bends pretty much permanent; they can be further bent, but no longer roll out under finger pressure.

Although we’re not yet at the point where we must reuse wires, I took this as an opportunity to improve my annealing hand: heat the wire almost to its melting point, hold it there for a few seconds, then let it cool slowly. The usual technique involves covering the aluminum with something like hand soap or permanent marker ink, heat until the soap / marker burns away, then let it air-cool. Unlike steel, there’s no need for quenching or tempering.

Blue Sharpie worked surprisingly well with a propane torch:

Armature wire - annealed straightened
Armature wire – annealed straightened

As far as I can tell after a few attempts, the pigment vanishes just below the annealing temperature and requires another pass to reach the right temperature. Sweep the flame steadily, don’t pause, and don’t hold the wire over anything melt-able.

Those wires (I cut the doubled wire apart) aren’t quite as soft as the original stock, but they rolled straight and are certainly good enough for our simple needs; they’re back in the Basement Laboratory Warehouse for future (re)use.

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.

MTD Snowthrower: Friction Wheel Tire Replacement

Late in last winter’s snowfall, our MTD snowthrower / snowblower ran low on get-up-and-go mobility, so I resolved to check inside before the next snowfall. What with one thing and another, time passed until, a few days before the first major snowfall of this winter season, I opened the bottom cover and found this mess:

Snowthrower friction wheel - worn in place
Snowthrower friction wheel – worn in place

Oops.

A diagram from the manual identifies the components:

MTD Snowblower - drive train - Fig 23
MTD Snowblower – drive train – Fig 23

The 8 HP gas engine spins the drive plate, which transfers some of those horses through the rubber tire on the friction wheel to the gear shaft, which turns the axle attached to the wheels. The shift lever (not shown) moves the friction wheel along the shaft to change the “gear ratio” setting the ground speed, with five positions to the right of the plate center going forward and two on the left going in reverse.

It’s a modern implementation of the classic Lambert friction drive transmission from a century ago. Cheap, effective, nothing wrong with it other than requiring regular inspection and preventive maintenance.

Unfortunately, the rubber tire seems undersized for the task and had completely worn away, leaving its steel rim to chew on the drive plate:

Snowthrower friction wheel - scarred drive plate
Snowthrower friction wheel – scarred drive plate

Of course, you’re supposed to inspect the situation more regularly than I (and, most likely, anyone) ever have. I vaguely recall replacing the tire once before and, being that type of guy, ordered two to have a spare on the shelf. Anyhow, it was in fine shape the last time I checked to see what shape it was in.

The manual recommends loosening (but not removing) the hex nut on the left side of the gear shaft:

Snowthrower drive gear shaft bearing
Snowthrower drive gear shaft bearing

Then “lightly tap the hex nut to dislodge the ball bearing”. Well, it’s a nylon lock nut, not a plain hex nut, which means pounding the crimp holding the nylon ring on the nut will destroy it. I whacked the end of the shaft with a plastic hammer to no avail, removed the nut & washer, and gave it a few careful shots with a 2 lb ball peen hammer, also to no avail.

The basic problem comes down to having the bearing mounted in what’s basically a sheet metal wall of no particular substance: banging on the shaft deflects the wall and moves the bearing along with the shaft. As far as I could tell, the shaft was stuck inside the bearing race, so I soaked it in pentrating oil while pondering the next step overnight.

A few more shots with the hammer convinced me that wasn’t going to work and would likely damage the threads, so I made a pair of Special Service Tools:

Snowthrower friction wheel - homebrew removal tools
Snowthrower friction wheel – homebrew removal tools

The smaller one fits around the threaded end of the shaft and inside the inner race to apply the impact directly to the shaft instead of the threads. The larger one fits on the inner race itself, in the expectation I would need to persuade it, but it wasn’t necessary. They both started life as iron pipe, covered in what looks like aluminumized paint for no reason we’ll ever know, and faced in the lathe.

The combination of penetrating oil, a proper SST, and some diligent whacking popped the shaft out of the bearing without damage. The friction wheel assembly then slid off the shaft with no resistance and the shaft and right-side bearing slid easily out of the frame. Once in the shop, gentle filing knocked the rust & burrs off the shaft and let it slide freely into the bearing.

The friction wheel clamps the tire with six bolts, three from each side so MTD can use a single part number for the halves:

Snowthrower friction wheel - screw pattern
Snowthrower friction wheel – screw pattern

It came apart easily, the new tire went on easily, the drive assembly went back together easily, and the blower cleared more than a foot of snow from the driveway:

Mary running snowthrower - 2020-12-17
Mary running snowthrower – 2020-12-17

Nothing can make maneuvering a snowblower easy, alas.

I briefly thought of refacing the drive plate, but I’m pretty sure it comes heartbreakingly close to Tiny Lathe’s limited swing. With two spare tires on the shelf, should the scarred plate chew up the new tire in one season, I’ll make better measurements.

MTD Snowthrower: Replacement Throttle Knob

The throttle knob on our MTD snowthrower (a.k.a. snowblower) cracked apart around its metal shaft when I pulled it upward. A temporary fix involving duct tape and cable ties sufficed to start the engine, although the usual intense vibration shook the knob loose somewhere along the driveway during the next hour.

Update: Found it!

Although I have no photographic evidence, I did make a few quick measurements:

Throttle Knob Dimension Doodles
Throttle Knob Dimension Doodles

It fits an MTD model E6A4E, but I suspect nearly all their engines have identical throttle shafts:

Snowthrower Throttle Knob - stem end - solid model
Snowthrower Throttle Knob – stem end – solid model

The only practical way to build the thing has it standing on the shaft end, surrounded by a brim to improve adhesion, so I added (actually, subtracted) a pair of holes for music-wire reinforcements:

Snowthrower throttle knob - reinforcing wires
Snowthrower throttle knob – reinforcing wires

It definitely has a stylin’ look, next to the original choke control knob:

Snowthrower throttle knob - installed
Snowthrower throttle knob – installed

I omitted the finger grip grooves for obvious reasons.

The slot-and-hole came out slightly smaller than the metal shaft and, rather than wait for epoxy to cure, I deployed a 230 W soldering gun (not a piddly temperature-controlled iron suitable for electronics) on the shaft and melted it into the knob.

More snow may arrive this week and I printed another knob just in case …

The OpenSCAD source code as a GitHub Gist:

// MTD Snowthrower Throttle Knob
// Ed Nisley KE4ZNU 2020-12-18
/* [Options] */
Layout = "Show"; // [Build, Show]
// Extrusion parameters
/* [Hidden] */
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
Throttle = [17.0,1.85,6.5]; // blade insertion, thickness, width
PaddleSize = [25,30,9];
PaddleRound = 4.0;
PaddleThick = 8.5;
StemDia = 13.0;
StemLength = 20.0;
PinDia = 1.6;
PinLength = PaddleSize.x + StemLength/2;
echo(str("Pin: ",PinLength," x ",PinDia," mm"));
//----------------------
// 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);
}
//----------------------
// Pieces
module Paddle() {
difference() {
hull() {
translate([PaddleSize.x/2,0,0]) {
for (i=[-1,1], j=[-1,1])
translate([i*(PaddleSize.x - PaddleRound)/2,j*(PaddleSize.y - PaddleRound)/2,0])
sphere(d=PaddleRound,$fn=12);
rotate([0,90,0]) rotate(180/12)
cylinder(d=PaddleThick,h=PaddleSize.x,,center=true,$fn=12);
}
translate([-StemLength,0,0])
rotate([0,90,0]) rotate(180/12)
cylinder(d=StemDia,h=Throttle.x,center=false,$fn=12);
}
translate([-StemLength,0,0])
cube([2*Throttle.x,Throttle.y,Throttle.z],center=true);
translate([-(StemLength + Protrusion),0,0])
rotate([0,90,0]) rotate(0*180/6)
PolyCyl(2*Throttle.y,Throttle.x,6);
for (j=[-1,1])
translate([-StemLength/2,j*PaddleSize.y/6,0])
rotate([0,90,0]) rotate(180/4)
PolyCyl(PinDia,PinLength,4);
}
}
//----------------------
// Build it
if (Layout == "Show")
Paddle();
if (Layout == "Build") {
translate([0,0,StemLength])
rotate([0,-90,0])
Paddle();
}