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

  • Bafang Brake Sensor Magnet Re-Re-Realignment

    Bafang Brake Sensor Magnet Re-Re-Realignment

    Several years and realignments later, the brake sensors on Mary’s Tour Easy became slightly mobile on the foam tape holding them to the brake lever mounts and, of course, anything affecting the sensor position relative to the magnet inevitably makes it overly sensitive to the brake lever position.

    For unknown reasons, the DPC-18 displays devote zero pixels to the brake sensor status, so the only indication of a brake sensor being active is when the motor cuts out. This is not to be tolerated, particularly in traffic, so both the sensors and their magnets are now remounted on fresh tape with careful attention to proximity.

    The magnet is snugged against the sensor when the lever is released:

    Tour Easy brake sensor gap - released
    Tour Easy brake sensor gap – released

    The activation point is about a third of the way to the fully pulled position:

    Tour Easy brake sensor gap - braking
    Tour Easy brake sensor gap – braking

    I would prefer having the magnet move parallel to the sensor’s long axis, but that’s just not the way these levers work.

    The left-side sensor cable is now snugged to the brake cables to keep it out of harm’s way as she installs & removes the water reservoir on the handlebars under the fairing:

    Tour Easy brake sensor cable - left
    Tour Easy brake sensor cable – left

    For symmetry, the right-side sensor cable sports a similar tiedown:

    Tour Easy brake sensor cable - right
    Tour Easy brake sensor cable – right

    Having recently discovered brake sensors with integral LED status indicators, I plan to update that whole mess in the near future.

  • Water Heater Anode Rod Replacement

    Water Heater Anode Rod Replacement

    The Bradford White AeroTherm heat pump water heater that Came With The House™ has been running for three years. Based on previous experience with the same City of Poughkeepsie water, it was time for a look at the anode rod.

    Which, as always, required the Greatest Ratchet with a foot of extensions getting the 1-1/16 inch socket down to the rod:

    Water Heater Powered Anode - socket wrench
    Water Heater Powered Anode – socket wrench

    Remember to:

    • Turn off the water heater power
    • Shut off the inlet and outlet valves
    • Open the drain valve to relieve the tank pressure
    • Only then loosen the anode rod

    Seeing the first few inches justified extracting the rod:

    Water Heater Powered Anode - old rod top
    Water Heater Powered Anode – old rod top

    Due to low overhead clearance and inauspicious pipe locations, I had to hacksaw the bottom third off to extract the rod:

    Water Heater Powered Anode - old rod overview
    Water Heater Powered Anode – old rod overview

    Water Heater Powered Anode - old rod bottom
    Water Heater Powered Anode – old rod bottom

    With the rod out, draining a few gallons of water into a pan didn’t produce any notable gravel, so I didn’t do a complete flush. Much to my delight, the drain has a ball valve!

    Given that this is a fancy water heater and I no longer regard anode rod maintenance as a fun-filled activity, I installed a Corro-Protec powered anode rod for cathodic protection without all the crud:

    Water Heater Powered Anode - Corro-Protec parts
    Water Heater Powered Anode – Corro-Protec parts

    The wall wart proclaims 15 mA with a green LED indicating it’s hard at work:

    Water Heater Powered Anode - Corro-Protec wall wart
    Water Heater Powered Anode – Corro-Protec wall wart

    The data plate on the back reports 24VDC 15mA, but, being me, I poked at it with a handful of resistors:

    Corro-Protec power supply measurements
    Corro-Protec power supply measurements

    The electronics could be as simple as a 24 VDC wall wart feeding a 425 Ω series resistor and a transistor switch lighting the LED for currents a little over 6 mA.

    A quick ohmmeter check shows the powered rod electrode is open-circuit with respect to the tank, as it should be without much applied voltage. Applying +24 VDC from the wall wart drives 15 mA into the water: the rod is hard at work.

    I’ll take a look at it in a year or so just to see what’s going on in there …

  • Nuheara IQbuds² MAX Charging Case Battery Replacement

    Nuheara IQbuds² MAX Charging Case Battery Replacement

    After five years of daily use, the lithium battery in the charging case of my Nuheara IQbuds² MAX they’re-not-hearing-aids earbuds began absorbing far more energy during its overnight recharges than the usual 100-ish mA·hr. This isn’t unexpected and, of course, Nuheara specifically mentions their batteries are not replaceable, so just toss the earbuds in the e-waste stream and buy another set.

    Having already replaced the smaller and harder-working lithium batteries in the earbuds twice, that’s just useless information … and something of a challenge.

    So, we begin.

    The lower part of the hinged case holds all the good stuff, with that blank hump obviously covering a great place for a battery:

    Nuheara IQBudsMax2 charging case - overview
    Nuheara IQBudsMax2 charging case – overview

    The bands around the earbuds are strips of gaffer tape holding their halves together after replacing their batteries. In this application, gaffer tape works much better than duct tape.

    There being no obvious screws, wedging a thumbnail into the joint just above the Micro-B USB jack proved promising. Doing the same on the other side produced enough of a gap to force a flat steel blade in there; further prying popped the snaps (red):

    Nuheara IQBudsMax2 charging case - snaps and glues
    Nuheara IQBudsMax2 charging case – snaps and glues

    The green ovals mark places where the case halves were glued together, accounting for some of the struggle.

    The two neodymium magnets in the front corners grab similar magnets in the upper cover to hold the case closed. The two magnets along the front edge grab magnets in the earbuds to hold them in the pockets, with more magnets flanking the gold-ish pogo pins visible in the first picture for better connections. If you’re concerned about the health effects of static magnetic fields, get over it.

    The battery was glued in place, so the first position after cracking the case apart must look like this:

    Nuheara IQBudsMax2 charging case - pried open
    Nuheara IQBudsMax2 charging case – pried open

    Yes. Yes, that is five years of earwax. You’ll note the lack of a seal at the bottom of the tunnels guiding the foam earbud tips: any and all particles falling off the earbuds have a direct path to the interior of the case.

    Easing the battery from its restraints reveals the data plate:

    Nuheara IQBudsMax2 charging case - snap and glue locationsNuheara IQBudsMax2 charging case - 802050 lithium cell
    Nuheara IQBudsMax2 charging case – snap and glue locationsNuheara IQBudsMax2 charging case – 802050 lithium cell

    The reflections in the pouch’s now-curved upper surface show the battery has become a Spicy Pillow.

    Somewhat to my surprise, 802050 batteries are available from the usual sources.

    Unwrapping several layers of kapton tape exposed the battery protection PCB:

    Nuheara IQBudsMax2 charging case - battery management PCB
    Nuheara IQBudsMax2 charging case – battery management PCB

    Comfortingly, the DW01 topmark on the upper IC says it’s a battery protection IC and the 8205A topmark on the lower IC indicates a dual MOSFET switching the battery current off during shutdowns. I did not trace the circuitry, but the white wire might control the DW01 CS terminal.

    The solder side:

    Nuheara IQBudsMax2 charging case - BMS solder joints
    Nuheara IQBudsMax2 charging case – BMS solder joints

    The new battery had a similar protection PCB with two wires. Rather than argue the point, I snipped its (welded!) battery tabs and transplanted the Nuheara PCB to the new battery, adding 30 AWG jumpers because the new tabs were shorter than the old ones:

    Nuheara IQBudsMax2 charging case - new battery installed
    Nuheara IQBudsMax2 charging case – new battery installed

    The strangely empty space to the right of the battery is just big enough for the protection PCB and all the kapton wrappings. The various plastic tabs around the battery hold it in place, so I did not renew its glue.

    Similarly, the four case snaps do a great job of holding the halves together, so they’re not glued, either.

    An overnight charge brought the case back to life and it’s once again working fine. However, the earbuds may be due for their third battery refresh.

    I love a happy ending!

  • Sherline vs. LinuxCNC 2.9.8

    Sherline vs. LinuxCNC 2.9.8

    Notes on finally getting the Sherline CNC mill operating in its new home, with a suitable Axis startup image:

    Sherline setup 2026-06
    Sherline setup 2026-06

    The gray countertop from its former home sits on foam strips soaking up a slight warp with enough isolation to keep things quiet.

    The gantry required the usual fiddling to make the cable hoist the Z axis directly upward, with the orange flag on the counterweight barely visible below the monitor.

    I recently touched the box of precision XYZ positioners and there might be something useful, albeit grossly overqualified, in there to simplify dropping the laser pointer beam directly through the spindle bore.

    A clean installation of LinuxCNC 2.9.8 on an ancient Dell Optiplex 9020 proceeded smoothly. Installing x11vnc let the rest of the proceedings happen from upstairs in the Comfy Chair. For unknown reasons, vinagre works better than Reminna as a VNC client, after recalling F11 enters / exits fullscreen mode.

    The mesaflash utility accompanying LinuxCNC 2.9.8 did not recognize the Mesa 6i25 card. Fetching & compiling the most recent version (3.5.17) cleared that hump and flashed the bitmap:

    sudo mesaflash --verbose --device 5i25 --write 5i25/configs/hostmot2/5i25_prob_rfx2.bit
    

    The 6i25 wants to be known as a 5i25, with its jumpers in their default positions:

    Mesa 6i25 - jumper locations
    Mesa 6i25 – jumper locations

    For unknown reasons, the button originally known as btn-trigger became btn-joystick for a while and has now reverted to btn-trigger. It’s labeled 1 in the four-button cluster:

    Logitech Dual-Action Gamepad - relabeled
    Logitech Dual-Action Gamepad – relabeled

    Which required changing the pin name in the Kicad library component:

    Sherline HAL schematic - Logitech button 1 name
    Sherline HAL schematic – Logitech button 1 name

    Which required converting the old Kicad library format into the new Kicad library format, a completely automatic process without, AFAICT, any unpleasant side effects.

    The new name fed into the schematic as expected, after minor fumbling while re-adding the modified component and setting its annotation number:

    Sherline HAL schematic - Logitech AZ button logic
    Sherline HAL schematic – Logitech AZ button logic

    The X axis home microswitch has (apparently) become more bouncy while it was idle, so I increased the number of samples before HAL sees a change in that GPIO input:

    Sherline HAL schematic - home switch debounce
    Sherline HAL schematic – home switch debounce

    The dbounce block runs in the servo thread at 1 m per tick, so those 20 samples take all of 20 ms while the X axis moves 0.15 mm. At some point I should apply a scope to that switch, but for now It Just Works™.

    Considerably to my surprise, compiling the modified Kicad schematic into a HAL file proceeded without incident, despite various Python updates in the last five years.

    Kicad produces an “intermediate XML file” containing the netlist data intended for a conversion / export program, which is basically what my Kicad-to-HAL lashup does. I told Kicad to use Bash’s true command as a converter:

    Sherline HAL schematic - netlist export
    Sherline HAL schematic – netlist export

    So I can run the Kicad-to-HAL converter manually:

    python ../Kicad\ Conversion/Kicad-to-HAL.py Sherline\ HAL\ -\ Logitech\ Gamepad\ jogging.xml Sherline.hal
    

    You could tell Kicad to run it and it would probably Just Work™, but I’m used to peering at the results and dinking with my program.

    Which produced a new Sherline.hal file that Just Worked™ with the existing Sherline.ini file containing the configuration constants.

    The Sherline has cut only air so far, but, as the man said, “E pur si muove.”

  • Power Switch Mollyguards

    Power Switch Mollyguards

    It turns out that dragging a USB cable across the top of the UPS for the PC (about to be) running the Sherline mill was enough to flip its flush-mounted hairtrigger power switch. Although I can’t recess the switch, adding a mollyguard should help:

    Mollyguard - UPS power switch
    Mollyguard – UPS power switch

    The power switches on the new outlet strips also seem unduly sensitive and a preemptive strike seemed in order:

    Mollyguard - outlet strip switch
    Mollyguard – outlet strip switch

    Two layers of 3 mm acrylic just barely clear the switch, but should prevent casual trips. AFAICT, the little hexagonal shape fills the hole for an indicator LED this strip doesn’t have.

    Yellow acrylic is deliberately more conspicuous than the bike control mollyguard.

  • Samsung Dishwasher Drying Fan Replacement

    Samsung Dishwasher Drying Fan Replacement

    The Samsung dishwasher (model DW80K7050US/AA 03) that Came With The House fails immediately after entering the Dry part of the cycle: a relay in the control PCB under the door goes doink, all the LEDS go off then on again, the countdown timer stops changing, and that repeats as long as you like.

    After considerable listening & pondering, I decided one event early in the Dry cycle involved starting a fan to vent the steam out of the interior. The wiring diagram shows the fan has a Fault wire: perhaps the fan has failed.

    The maintenance manual shows different fans in three different places, although the control board has a connector for only one. By process of elimination, I found the fan atop the cabinet:

    Samsung dishwasher - top view
    Samsung dishwasher – top view

    The cable from the fan in the vented compartment on the left burrows under the gray duct, around its back side, and plugs into the small white connector on the right. You must ease the cable from a row of hooks guiding it around the back of the duct, which requires slightly lifting the duct.

    Unhook the two metal straps, remove four screws from the black vent, and lift it off the top to reveal the duct outlet pores:

    Samsung dishwasher - fan duct - overview
    Samsung dishwasher – fan duct – overview

    Remove four more screws, lift the fan duct assembly just a little bit, and pry open three latches around the fan compartment with a consumer electronics case-cracking tool:

    Samsung dishwasher - fan housing
    Samsung dishwasher – fan housing

    The new fan (on the right) looks very much like the OEM fan (on the left), even though it’s the $15 version rather than the $150 version you might buy from similar randomly named sellers if you were so inclined:

    Samsung dishwasher - OEM vs new fan
    Samsung dishwasher – OEM vs new fan

    Detach the old fan & its cable, drop the new fan in place, snake its cable, plug its plug, and install All The Things in reverse order.

    Unfortunately, after shoving the dishwasher back into its cubby, the new fan didn’t change the failure at all.

    I hitched the old fan up to the bench supply and it spun just like it should. Wiring the Fault wire to a 5 V supply through a resistor shows it’s the usual tachometer signal pulsing as the rotor spins.

    Which means the next step requires more pondering and PCB probing. The failure is too consistent to be a Heisenbug, but maybe something shook loose in there.

  • Outlet Strip Bench Mount

    Outlet Strip Bench Mount

    A spate of tidying-up led to mounting an outlet strip along the back of a bench:

    Outlet Bench Mount - installed
    Outlet Bench Mount – installed

    Rather than drill holes into the top of the bench for those screws, they fit into M4 brass inserts heat-staked into the brackets:

    Outlet Bench Mount - show view
    Outlet Bench Mount – show view

    The holes for those inserts aren’t centered side-to-side on the brackets, because the screw holes aren’t centered on the bent-steel angles forming the outlet strip endplates.

    The bottom arm on the brackets probably isn’t necessary, but they kept the outlet strip from crawling away while I match-drilled two holes for the screws into the side of the benchtop.

    For obvious reasons, the brackets print on their sides:

    Outlet Bench Mount - build view
    Outlet Bench Mount – build view

    Another outlet strip from a different manufacturer is, of course, different, but changing three parameters in the OpenSCAD program summons a different bracket from the vasty digital deep:

    Outlet Bench Mount - different brand
    Outlet Bench Mount – different brand

    Parametric modeling and a 3D printer are exactly the right hammers for the job …

    The OpenSCAD source code as a GitHub Gist:

    // Shower soap dish
    // Ed Nisley – KE4ZNU
    // 2026-06-04
    include <BOSL2/std.scad>
    Layout = "Show"; // [Show,Build]
    /* [Hidden] */
    HoleWindage = 0.2;
    Protrusion = 0.1;
    NumSides = 3*3*4;
    Gap = 10.0/2;
    $fn=NumSides;
    ID = 0;
    OD = 1;
    LENGTH = 2;
    BenchThick = 21.0; // workbench top
    ScrewOD = 4.0; // into edge of bench
    Insert = [4.0,5.5,10.0]; // robust M4 insert
    WallThick = 10.0;
    BaseThick = 10.0;
    OutletBase = [15.0,40.0];
    HoleOffset = 6.5; // from outside edge of bracket
    HoleOC = 24.0;
    MountOA = [OutletBase.x,OutletBase.y,BenchThick + Insert[LENGTH] + 1.0 + BaseThick];
    //———-
    // Build it
    module Mount() {
    difference() {
    cuboid(MountOA,rounding=1.0,anchor=BOTTOM + BACK);
    up(BaseThick)
    fwd(WallThick)
    cuboid([2*MountOA.x,MountOA.y,BenchThick],anchor=BOTTOM + BACK);
    up(BaseThick + BenchThick/2) back(Protrusion)
    ycyl(OutletBase.y,d=ScrewOD,circum=true,$fn=6,anchor=BACK);
    for (j=[-1,1])
    fwd(MountOA.y/2 + j*HoleOC/2)
    right(HoleOffset – MountOA.x/2)
    up(MountOA.z + Protrusion)
    cyl(Insert[LENGTH],d=Insert[OD],circum=true,$fn=6,anchor=TOP);
    }
    }
    //———-
    // Build it
    if (Layout == "Show") {
    left(Gap + MountOA.x/2)
    Mount();
    right(Gap + MountOA.x/2)
    xflip() Mount(); // mirror for the other end of the outlet strip
    }
    if (Layout == "Build") {
    left(MountOA.z/2)
    up(MountOA.x/2)
    yrot(90)
    Mount();
    fwd(1.5*MountOA.y)
    left(MountOA.z/2 – BenchThick/2 – Insert[LENGTH]/2)
    zrot(180)
    up(MountOA.x/2)
    yrot(-90)
    xflip() Mount(); // mirror for the other end of the outlet strip
    }