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: Machine Shop

Mechanical widgetry

  • Kenmore Progressive Vacuum Cleaner vs. Classic Electrolux Dust Brush

    Vacuum cleaner dust brushes, separated by millimeters and decades:

    Kenmore vs adapted Electrolux dust brushes
    Kenmore vs adapted Electrolux dust brushes

    The bulky one on the left came with our new Kenmore Progressive vacuum cleaner. It’s fine for dust on a flat horizontal or vertical surface and totally useless for dust on actual objects. It’s supposed to snap around the handle at the end of the cleaner’s flexy hose, where it helps make the entire assembly too large and too clumsy, or on the end of the “wand”, where it’s at the wrong angle. The bonus outer shell slides around the stubby bristles in the unlikely event they’re too long for the flat surface at hand.

    The brush on the right emerged from the Box o’ Electrolux Parts that Came With The House™, must be half a century old, and consists of a cast aluminum lump with various holes milled into it, adorned with luxuriously long and flexible horsehair. Suffice it to say they don’t make ’em like that any more. Heck, they probably don’t make horses with hair like that any more, either.

    The blue plastic adapter atop the aluminum ball looks like you’d expect by now:

    Electrolux Brush Adapter
    Electrolux Brush Adapter

    The short snout fits neatly into the space available inside the ball. The abrupt ledge at the top of the snout, of course, didn’t work well; I rushed the design for a show-n-tell.

    The OpenSCAD source code (as a Github gist) bevels that ledge and tweaks the interior air channel a bit:

    // Kenmore vacuum cleaner nozzle adapters
    // Ed Nisley KE4ZNU December 2015
    // Layout options
    Layout = "LuxBrush"; // MaleFitting CoilWand FloorBrush CreviceTool ScrubbyTool LuxBrush
    //- Extrusion parameters must match reality!
    // Print with +1 shells and 3 solid layers
    ThreadThick = 0.25;
    ThreadWidth = 0.40;
    HoleWindage = 0.2;
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    Protrusion = 0.1; // make holes end cleanly
    //———————-
    // Dimensions
    ID1 = 0; // for tapered tubes
    ID2 = 1;
    OD1 = 2;
    OD2 = 3;
    LENGTH = 4;
    OEMTube = [35.0,35.0,41.7,40.5,30.0]; // main fitting tube
    EndStop = [OEMTube[ID1],OEMTube[ID2],47.5,47.5,6.5]; // flange at end of main tube
    FittingOAL = OEMTube[LENGTH] + EndStop[LENGTH];
    $fn = 12*4;
    //———————-
    // 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);
    }
    //——————-
    // Male fitting on end of Kenmore tools
    // This slides into the end of the handle or wand and latches firmly in place
    module MaleFitting() {
    Latch = [40,11.5,5.0]; // rectangle latch opening
    EntryAngle = 45; // latch entry ramp
    EntrySides = 16;
    EntryHeight = 15.0; // lower edge on *inside* of fitting
    KeyRadius = 1.0;
    translate([0,0,6.5])
    difference() {
    union() {
    cylinder(d1=OEMTube[OD1],d2=OEMTube[OD2],h=OEMTube[LENGTH]); // main tube
    hull() // insertion guide
    for (i=[-(6.0/2 – KeyRadius),(6.0/2 – KeyRadius)],
    j=[-(28.0/2 – KeyRadius),(28.0/2 – KeyRadius)],
    k=[-(26.0/2 – KeyRadius),(26.0/2 – KeyRadius)])
    translate([(i – (OEMTube[ID1]/2 + OEMTube[OD1]/2)/2 + 6.0/2),j,(k + 26.0/2 – 1.0)])
    sphere(r=KeyRadius,$fn=8);
    translate([0,0,-EndStop[LENGTH]]) // wand tube butts against this
    cylinder(d=EndStop[OD1],h=EndStop[LENGTH] + Protrusion);
    }
    translate([0,0,-OEMTube[LENGTH]]) // main bore
    cylinder(d=OEMTube[ID1],h=2*OEMTube[LENGTH] + 2*Protrusion);
    translate([0,-11.5/2,23.0 – 5.0]) // latch opening
    cube(Latch);
    translate([OEMTube[ID1]/2 + EntryHeight/tan(90-EntryAngle),0,0]) // latch ramp
    translate([(Latch[1]/cos(180/EntrySides))*cos(EntryAngle)/2,0,(Latch[1]/cos(180/EntrySides))*sin(EntryAngle)/2])
    rotate([0,-EntryAngle,0])
    intersection() {
    rotate(180/EntrySides)
    PolyCyl(Latch[1],Latch[0],EntrySides);
    translate([-(2*Latch[0])/2,0,-Protrusion])
    cube(2*Latch[0],center=true);
    }
    }
    }
    //——————-
    // Refrigerator evaporator coil wand
    module CoilWand() {
    union() {
    translate([0,0,50.0])
    rotate([180,0,0])
    difference() {
    cylinder(d1=EndStop[OD1],d2=42.0,h=50.0);
    translate([0,0,-Protrusion])
    cylinder(d1=35.0,d2=35.8,h=100);
    }
    translate([0,0,50.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Refrigerator evaporator coil wand
    module FloorBrush() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=32.4,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=32.4,d2=30.7,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=28.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Crevice tool
    module CreviceTool() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=32.0,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=32.0,d2=30.4,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=28.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Mystery brush
    module ScrubbyTool() {
    union() {
    translate([0,0,60.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=31.8,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=31.8,d2=31.0,h=50.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=26.0,d2=24.0,h=100);
    }
    translate([0,0,60.0 – Protrusion])
    MaleFitting();
    }
    }
    //——————-
    // Electrolux brush ball
    module LuxBrush() {
    union() {
    translate([0,0,30.0])
    rotate([180,0,0])
    difference() {
    union() {
    cylinder(d1=EndStop[OD1],d2=30.8,h=10.0);
    translate([0,0,10.0 – Protrusion])
    cylinder(d1=30.8,d2=30.0,h=20.0 + Protrusion);
    }
    translate([0,0,-Protrusion])
    cylinder(d1=25.0,d2=23.0,h=30 + 2*Protrusion);
    }
    translate([0,0,30.0 – Protrusion])
    MaleFitting();
    }
    }
    //———————-
    // Build it!
    if (Layout == "MaleFitting")
    MaleFitting();
    if (Layout == "CoilWand")
    CoilWand();
    if (Layout == "FloorBrush")
    FloorBrush();
    if (Layout == "CreviceTool")
    CreviceTool();
    if (Layout == "ScrubbyTool")
    ScrubbyTool();
    if (Layout == "LuxBrush")
    LuxBrush();

    That’s  supposed to prevent the WordPress post editors from destroying the formatting…

  • Logitech M305 Mouse Switch Cleaning

    While installing Mint on the Lenovo Q150, I discovered that the right button on the (long disused) Logitech M305 wireless mouse wasn’t working. After replacing the batteries (always check the batteries), it still didn’t work, so I peeled the four slippery feet off the bottom, removed the screws, and confronted the interior:

    Logitech M305 mouse - interior
    Logitech M305 mouse – interior

    Much to my surprise, the button switches had removable covers:

    Logitech M305 mouse - switch disassembly
    Logitech M305 mouse – switch disassembly

    I put a minute drop of DeoxIT Red on a slip of paper, ran it between both pairs of contacts, removed a considerable amount of tarnish, reassembled in reverse order, and it’s all good again.

    The glue on the back of the slippery feet didn’t like being peeled off, so I expect they’ll fall off at some point.

    It’s much easier to drive a GUI with three functional buttons…

    [Update: Long-time commenter Raj notes:

    I always had problem with the middle button. I have replaced them a few times and learnt that they come with different operating pressures. The soft ones are hard to come by. I found an alternate in the PTT switches on Yaesu handies in my junk.

    That’s the blocky switch to the left of the shapely wheel cutout.]

  • Chiplotle: Better RTS-CTS Handshake Hackage

    With hardware handshaking in full effect, the Chiplotle routine that sends data to the HP 7475A plotter doesn’t need to sleep, because the Linux serial handlers take care of that under the hood. Rather than simply comment that statement out, as I did before, it’s better to test the configuration and only sleep when needed:

    The routine that extracts values from ~/.chiplotle/config.py is already included (well, imported) in the distribution’s baseplotter.py file, so all we need is a test for (the lack of) hardware handshaking:

       def _write_string_to_port(self, data):
          ''' Write data to serial port. data is expected to be a string.'''
          #assert type(data) is str
          if not isinstance(data, basestring):
             raise TypeError('string expected.')
          data = self._filter_unrecognized_commands(data)
          data = self._slice_string_to_buffer_size(data)
          for chunk in data:
             if not get_config_value('rtscts'):
                 self._sleep_while_buffer_full( )
             self._serial_port.write(chunk)
    

    The wisdom of reading a file inside the innermost loop of the serial data output routine may be debatable, but:

    • The output is 9600 b/s serial data
    • The expected result is that we’re about to wait
    • Plenty of smart folks have improved file I/O, so the read is probably a cache hit

    For all I know, it doesn’t actually read a file, but consults an in-memory data structure. Works well enough for me, anyhow.

    The configuration file I’ve been using all along looks like this (minus most of the comments):

    # -*- coding: utf-8 -*-
    serial_port_to_plotter_map = {'/dev/ttyUSB0' : 'HP7475A'}
    
    ## Serial connection parameters.
    ## Set your plotter to match these values, or vice versa..
    baudrate = 9600
    bytesize = 8
    parity = 'N'
    stopbits = 1
    timeout = 1
    xonxoff = 0
    rtscts = 1
    
    
    ## Maximum wait time for response from plotter.
    ## Every time the plotter is queried, Chiplotle will wait for
    ## a maximum of `maximum_response_wait_time` seconds.
    maximum_response_wait_time = 4
    
    
    ## Set to True if you want information (such as warnings)
    ## displayed on the console. Set to False if you don't.
    verbose = True
    

    That’s much prettier…

  • Gorilla Glue: Cured in the Bottle

    So the dishwasher ate another rack protector, which happens a few times a year. I’m getting low on spares, so maybe it’s time to run off a few in cyan PETG to see if the cute support structure will still be removable:

    Dishwasher rack protector - support model
    Dishwasher rack protector – support model

    Anyhow, this time I used urethane glue, because the last of the acrylic caulk went into another project. I store the Gorilla Glue bottle upside-down so the entire top doesn’t cure solid, but:

    Gorilla Glue - cured in bottle
    Gorilla Glue – cured in bottle

    Usually, it’s just cured in the snout. This time, the layer across the bottom was a few millimeters thick and the glue below seemed rather thick. I tossed the solid lump, slobbered a dab of thick goo on the dishwasher rack, jammed the new protector in place, replaced the cap, and declared victory.

    That’s why I no longer buy that stuff in The Big Bottle…

     

  • Squidwrench Vending Machine: OEM Wiring Diagram

    An old vending machine in need of rebooting may provide fodder for some electronics tutorials at Squidwrench. To that end, here’s the OEM wiring diagram pasted inside the door:

    SqWr Vending Machine - OEM Wiring Diagram
    SqWr Vending Machine – OEM Wiring Diagram

    That’s endured a perspective transformation and a bit of contrast stretching; it looks awful, but being able to view it without squatting inside the machine makes it much easier to read…

    Each selector and motor cycle switch pair interact with the motor thusly:

    Vending Machine - Switches and Motor Doodle
    Vending Machine – Switches and Motor Doodle

    All of the motors have one side connected directly to the 24 VAC power transformer. The wiring diagram shows a pair of transformers in parallel, which seems odd.

    The Selector switches (an array of 30 on the front panel, with one broken that will surely call for some 3D printing) are in series, so the lowest-numbered one wins; the NO terminal of each Selector switch  goes directly to the control box. Pressing a switch connects the Red·Orange wire on the C terminal of the first switch to the control box on the same wire as the corresponding motor lead.

    Assuming the Motor Cycle switch parks in the NC position, it will disconnect the Orange wire from the Orange·Black wire and connect it to the lower motor lead and the Select switch (which may or may not be pressed by then), although we don’t know the timing. There’s surely a cam on the motor shaft.

    Some possibly relevant patents, found after a brief search with the obvious keywords:

  • Hard Drive Platter Mood Light: Thermal (Mis)Management

    So another knockoff Neopixel started flickering and its blue LED went dark:

    Hard Drive Mood Light - blue failure
    Hard Drive Mood Light – blue failure

    Squirting it with circuit cooler brought it back to life, albeit briefly, so it’s a real thermal failure. OK, after I get smacked upside the head twice, I can recognize a problem when I see it.

    I removed the top cover and jammed a themocouple into the screw hole in the middle of the pillar:

    Mood Light - thermocouple location
    Mood Light – thermocouple location

    A folded tissue weighted down with random desktop junk kept the breeze out of the interior:

    Mood Light - PWM 128 temperature measurement
    Mood Light – PWM 128 temperature measurement

    If the middle of the column hits 50 °C, what’s it like inside the 5050 packages with all those LEDs blazing away? Looks like I’ve been cooking those poor knockoff Neopixels to death.

    The temperature is 50 °C with the LEDs running at maximum PWM = 128. Reducing the maximum PWM to 64 reduces the core to 30 °C and that dead blue LED springs back to life.

    Figuring each LED package dissipate 250-ish mW at full throttle, that’s 120 mW at PWM 128 / 60 mW at PWM 64. The set of 12 packages dissipates 1.4 W / 750 mW, so, in a 22 °C room, the thermal coefficient is up around 10 to 20 °C/W, which is somewhere between bad and awful. Running the LEDs at full throttle obviously isn’t an option and even half-throttle really doesn’t work.

    So, OK, mounting LED strips on a clever 3D printed plastic column with zero air circulation isn’t nearly as smart an idea as I thought: barely more than a watt burns right through the redline.

    The Neopixel specs have nothing to say about the thermal coefficient from the LED junctions to the package leads, but cooling the copper conductors in the flex PCB can’t possibly hurt.

    No, I do not want to CNC machine an aluminum pillar with little tabs on the platter for better heatsinking. It would be an interesting design project, though.

  • Whirlpool Refrigerator Drawer Strut Tab Replacement

    The tab supporting the strut with the center slides for the lower drawers in our Whirlpool refrigerator broke of its own accord. This is a problem of long standing, somewhat exacerbated by the fact that lifting the strut will break the tab without much effort at all, but this time the drawers pulled the strut downward hard enough to not only break the tab, but also tear the small tabs that align the bracket right out of the frame.

    While pondering the problem, I glued the broken chunk back into the frame:

    Refrigerator Drawer Strut - clamping front plate
    Refrigerator Drawer Strut – clamping front plate

    We agreed that, after nigh onto two decades, it would be OK to swap the position of the two drawers, so as to let the strut use the undamaged part of the frame seen below. Presumably, we’ll eventually get used to having the apples on the right and the veggies on the left.

    But it was obvious Something Serious Had To Be Done about the tab.

    The tab should align like this inside the frame:

    Refrigerator Drawer Strut Tab - alignment
    Refrigerator Drawer Strut Tab – alignment

    The rightmost part of the tab rests atop a U-shaped metal bar that also supports and stiffens the entire front of the frame, but cantilevering the weight of both drawers on that extended tab overpowered my last attempt at making a glue joint. Soooo, I decided to build a (wait for it …) 3D printed part that screws firmly to the front of the strut.

    The first step involved introducing the strut to Mr Belt Sander to strip the wreckage of the OEM tab from the front end (visible through the opening) and smooth things out, then measuring the remainder. The locating flange inside the frame almost defeated me, but eventually I found a tool that fit inside the strut opening and around the flange:

    Refrigerator Drawer - measuring flange
    Refrigerator Drawer – measuring flange

    Which produced a sketch of the key dimensions:

    Refrigerator Drawer Strut - Dimension Doodles
    Refrigerator Drawer Strut – Dimension Doodles

    Which became an extruded polygon with a few holes punched in the side:

    Refrigerator Shelf Strut Tab - solid model
    Refrigerator Shelf Strut Tab – solid model

    Building it standing up wraps the plastic threads around the entire tab and stacks the layers along the length of the tab. Doing it lying down in the obvious hump-up orientation would put the layers parallel to the bottom surface, where they can pull apart under load.

    The key innovation here involves being willing to assemble the tab to the strut in situ, without insisting it fit through the frame opening and be more-or-less easily removable. That let me bulk up the tab to match the end of the strut, fill the entire frame opening with plastic, and get enough bulk for a pair of 4-40 screws that, being loaded in shear, should withstand the weight of all those fruits & veggies in the drawers.

    The screws simply thread into the holes in the tab, without benefit of tapping. The OpenSCAD code now includes a pair of nut traps, but I’m hoping they won’t be needed.

    The new tab really does fill the space available:

    Refrigerator Drawer Strut - new tab in place
    Refrigerator Drawer Strut – new tab in place

    The OpenSCAD code now moves the notch half a millimeter further away from the strut to center it over the ridge. What’s not obvious is how the frame slants toward the tab over the U-bar: the tab just barely clears and probably should have a tapered nose. You may add that if you like.

    The U-shaped bar constrains the tab pretty firmly and supports the end, which should now be plump enough to withstand the forces involved. The screws sit horizontally with the frame installed and can’t pull out, which is why I think they can get along without nut traps.

    It’s built in cyan PETG with three perimeter threads and 40% 3D Honeycomb fill, making it essentially a solid block of plastic; it’ll be interesting to see what fails next.

    The OpenSCAD source code, which I hammered out in a white-hot fury:

    // Refrigerator Shelf Strut Tab
    // Ed Nisley KE4ZNU December 2015
    
    //- Extrusion parameters must match reality!
    
    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
    
    TabSize = [20.0,12.0,35.0];							// length from bracket, height, width along front
    
    SlotSize = [3.0,7.0];
    SlotX = 7.0;
    
    TabProfile = [
    	[0,0],
    	[12,0],	[12,7.0],
    	[TabSize[0],7.0], [TabSize[0],TabSize[1]],
    	[SlotX + SlotSize[0]/2,TabSize[1]],
    	[SlotX + SlotSize[0]/2,5.0], [SlotX - SlotSize[0]/2,5.0],
    	[SlotX - SlotSize[0]/2,TabSize[1]],
    	[0,TabSize[1]]
    ];
    
    ScrewY = 7.0;
    ScrewOC = 25.0;
    ScrewOD = 2.5;
    
    NutOD = 6.6;					// across flats
    NutThick = 2.5;
    
    //----------------------
    // 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);
    }
    
    //----------------------
    // Build it
    
    difference() {
    	linear_extrude(height=TabSize[2],convexity=4)
    		polygon(points=TabProfile);
    	
    	for (i=[-1,1]) {
    		translate([-Protrusion,ScrewY,i*ScrewOC/2 + TabSize[2]/2])
    			rotate([0,90,0])
    				rotate(180/6)
    					PolyCyl(ScrewOD,SlotX,6);
    		translate([SlotX - SlotSize[0]/2 - NutThick - Protrusion,ScrewY,i*ScrewOC/2 + TabSize[2]/2])
    			rotate([0,90,0])
    				rotate(180/6)
    					PolyCyl(NutOD,NutThick + SlotSize[0],6);
    	}
    }
    

    Maybe that’ll last until we finally scrap out the refrigerator…