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

  • Kenmore 158 UI: Power & Data Cable

    The Arduino Mega behind the LCD panel communicates serially through the Serial1 hardware, leaving the USB connection available for its usual console + program loading functions. The cable also carries +7 VDC for the Mega’s on-board regulator, plus a few bits that might prove useful, and enough grounds to be meaningful.

    The pinout on the DE-9 female back-panel connector:

    1. TX (Mini -> Mega)
    2. RX (Mini <- Mega)
    3. Current sense amp
    4. D4 Enable ATX
    5. Gnd
    6. +7V regulator
    7. Gnd
    8. Gnd
    9. Gnd

    Which looks like this:

    Kenmore 158 UI - cable at motor controller
    Kenmore 158 UI – cable at motor controller

    One could argue that I should use insulation-displacement connectors and pin headers, but there’s something to be said for a bit of meditative hand-soldering.

    The 7 V supply drops about 90 mV through its slightly too thin wire. With current around 100 mA, that works out to 900 mΩ, including all the connectors and gimcrackery along the way. Close enough.

    More cogently, one could argue that I should have used a DE-9 male connector, so as to remove the possibility of swapping the cables. So it goes. The pinout attempts to minimize damage, but ya never know.

    The green jumper on the Mini’s serial pins reminds me to unplug the UI cable, lest I plug the USB adapter into it and put the serial drivers in parallel.

    The 7 V regulator stands over on the left, powering both the Arduino Pro Mini and the Mega + LCD panel. My thumb tells me that piddly little heatsink isn’t quite up to its new responsibilities, unlike the now vastly overqualified heatsink on the ET227. On the other paw, that’s why I used a pre-regulator: so that same heat isn’t burning up the SMD regulators on the Arduino PCBs. Time to rummage in the Big Box o’ Heatsinks again.

  • Adafruit Touch-screen TFT LCD Rotation

    The alert reader will have noted that the Kenmore 158 UI twisted around to a new orientation atop its fancy holder, with the USB port now poking out from the right side:

    Kenmore 158 UI - PCB holder
    Kenmore 158 UI – PCB holder

    That lets me position the whole affair to the right of the sewing machine, in what seems to be its natural position, without having the cable form a loop that would push it off the platform. It’s not entirely clear how we’ll keep a straight cable from pulling it off, but that’s in the nature of fine tuning.

    Anyhow, rotating the LCD isn’t a big deal, because the Adafruit library does all the heavy lifting:

    // LCD orientation: always landscape, 1=USB upper left / 3=USB lower right
    #define LCDROTATION 3
    
    ... snippage ...
    tft.begin();
    tft.setRotation(LCDROTATION);	// landscape, 1=USB upper left / 3=USB lower right
    

    Flipping the touch screen coordinates required just interchanging the “to” bounds of the map() functions, with a conditional serving as institutional memory in the not-so-unlikely event I must undo this:

    #if LCDROTATION == 1
    	p->x = map(t.y, TS_Min.y, TS_Max.y, 0, tft.width());	// rotate & scale to TFT boundaries
    	p->y = map(t.x, TS_Min.x, TS_Max.x, tft.height(), 0);	//   ... USB port at upper left
    #elif LCDROTATION == 3
    	p->x = map(t.y, TS_Min.y, TS_Max.y, tft.width(), 0);	// rotate & scale to TFT boundaries
    	p->y = map(t.x, TS_Min.x, TS_Max.x, 0, tft.height());	//   ... USB port at lower right
    #endif
    

    And then It Just Worked.

  • Arduino Mega PCB Holder

    Flushed with success from making the boost power supply mount, here’s a holder for the Arduino Mega that’s supporting the Kenmore 158 sewing machine UI:

    Kenmore 158 UI - PCB holder
    Kenmore 158 UI – PCB holder

    The solid model shows two screws holding the PCB in place:

    Arduino Mega PCB Mount
    Arduino Mega PCB Mount

    I decided to edge-clamp the board, rather than fuss with the built-in screws, just because 3D printing makes it so easy.

    Of course, the UI needs a real case that will hold it at an angle, so as to make the LCD and touch screen more visible and convenient; this mount just keeps the PCB up off the conductive surface of the insulating board we’re using in lieu of a Real Sewing Platform.

    This sewing machine project involves a lot of parts…

    The OpenSCAD source code:

    // PCB mounting bracket for Arduino Mega
    // Ed Nisley - KE4ZNU - January 2015
    
    Layout = "Build";			// PCB Block Mount Build
    
    //- Extrusion parameters must match reality!
    //  Print with 4 shells and 3 solid layers
    
    ThreadThick = 0.20;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;			// extra clearance
    
    Protrusion = 0.1;			// make holes end cleanly
    
    AlignPinOD = 1.70;			// assembly alignment pins: filament dia
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    X = 0;						// useful subscripts
    Y = 1;
    Z = 2;
    
    //----------------------
    // Dimensions
    
    inch = 25.4;
    
    Tap4_40 = 0.089 * inch;
    Clear4_40 = 0.110 * inch;
    Head4_40 = 0.211 * inch;
    Head4_40Thick = 0.065 * inch;
    Nut4_40Dia = 0.228 * inch;
    Nut4_40Thick = 0.086 * inch;
    Washer4_40OD = 0.270 * inch;
    Washer4_40ID = 0.123 * inch;
    
    PCBoard = [102,54,IntegerMultiple(1.8,ThreadThick)];
    
    BottomParts = [[2.5,-5.0,0,0],				// xyz offset of part envelope
    				[96,80,IntegerMultiple(5.0,ThreadThick)]];			// xyz envelope size (z should be generous)
    
    Margin = IntegerMultiple(Washer4_40OD,ThreadWidth);
    
    MountBase = [PCBoard[X] + 2*Margin,
    			PCBoard[Y] + 2*Margin,
    			IntegerMultiple(5.0,ThreadThick) + PCBoard[Z] + BottomParts[1][Z]
    			];
    echo("Mount base: ",MountBase);
    
    ScrewOffset = Clear4_40/2;
    
    Holes = [									// PCB mounting screw holes: XY + rotation
    		[Margin - ScrewOffset,MountBase[Y]/2,180/6],
    		[MountBase[X] - Margin + ScrewOffset,MountBase[Y]/2,180/6],
    		];
    
    CornerRadius = Washer4_40OD / 2;
    
    //----------------------
    // 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);
    }
    
    module ShowPegGrid(Space = 10.0,Size = 1.0) {
    
      RangeX = floor(100 / Space);
      RangeY = floor(125 / Space);
    
    	for (x=[-RangeX:RangeX])
    	  for (y=[-RangeY:RangeY])
    		translate([x*Space,y*Space,Size/2])
    		  %cube(Size,center=true);
    
    }
    
    //----------------------
    // Build things
    
    module PCB() {
    
    	union() {
    		cube(PCBoard);
    		translate(BottomParts[X] - [0,0,BottomParts[1][Z]])
    			cube(BottomParts[Y] + [0,0,Protrusion]);
    	}
    
    }
    
    module Block() {
    	translate([MountBase[X]/2,MountBase[Y]/2,0])
    		hull()
    			for (i = [-1,1], j = [-1,1])
    				translate([i*(MountBase[X]/2 - CornerRadius),j*(MountBase[Y]/2 - CornerRadius)],0)
    					cylinder(r=CornerRadius,h=MountBase[Z] - Protrusion,$fn=8*4);
    }
    
    module Mount() {
    
    	difference() {
    		Block();
    
    		translate([MountBase[X]/2 - PCBoard[X]/2 + BottomParts[0][X] - Protrusion,
    					-MountBase[Y]/2,
    					MountBase[Z] - PCBoard[Z] - BottomParts[1][Z]])
    			cube([BottomParts[1][X] + 2*Protrusion,
    					2*MountBase[Y],
    					2*BottomParts[1][Z]]);
    
    		translate([MountBase[X]/2 - PCBoard[X]/2,		// PCB recess
    					MountBase[Y]/2 - PCBoard[Y]/2,
    					MountBase[Z] - PCBoard[Z]])
    			PCB();
    		for (h = Holes) {
    			translate([h[X],h[Y],-Protrusion]) rotate(h[Z])
    				PolyCyl(Tap4_40,MountBase[Z] + 2*Protrusion,6);
    		}
    	}
    
    }
    
    ShowPegGrid();
    
    if (Layout == "PCB")
    	PCB();
    
    if (Layout == "Block")
    	Block();
    
    if (Layout == "Mount")
    	Mount();
    
    if (Layout == "Build")
    	translate([-MountBase[X]/2,-MountBase[Y]/2,0])
    	Mount();
    
  • Monthly Science: Well Pit Temperature

    From a datalogger hanging on a string in the well pit, about three feet underground, in December:

    Well Pit - 2014-12 - min size
    Well Pit – 2014-12 – min size

    The temperatures continue downward in January:

    Well Pit - 2015-01 - min size
    Well Pit – 2015-01 – min size

    The corresponding attic air temperature record for January ends early:

    Attic - Insulated Box - Maxell battery failure
    Attic – Insulated Box – Maxell battery failure

    When the air temperature dropped to +11 °F in the early hours of 17 January 2015, the well pit hit 35.5 °F. It was just over 35 °F in the wee hours of 29 January 2015, but the attic logger gave up as the battery voltage declined to 2.8 V.

    Evidently, the new Maxell CR2032 lithium cells don’t do well in extreme cold. They’re rated to -20 °C = -4 °F, but that spec applies for a very low load that surely doesn’t include blinking a red LED.

    I’ll take a look at that logger in a few days, then hack a pair of AA cells on the back if it’s dead again. Alkaline cells aren’t very good in cold weather, either, but they may have a better minimum voltage.

    Or it’s just another batch of dud CR2032 cells…

  • Kenmore 158 Needle LEDs: First Light

    With the boost converter mounted and the needle LEDs wired up:

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

    The Kenmore 158 sewing machine crash test dummy has plenty of light:

    Kenmore 158 LED Lighting - first light
    Kenmore 158 LED Lighting – first light

    Well, as long as you don’t mind the clashing color balance. The needle LEDs turned out warmer than I expected, but Mary says she can cope. I should build a set of warm-white LED strips when it’s time to refit her real sewing machine and add another boost supply to drive them at their rated current.

    Much to our relief, the two LEDs at the needle don’t cast offensively dark shadows:

    Kenmore 158 LED Lighting - detail
    Kenmore 158 LED Lighting – detail

    All in all, it looks pretty good.

  • Generic PCB Holder: Boost Power Supply

    The DC-DC boost power supply for the LED needle lights has four mounting holes, two completely blocked by the heatsink and the others against components with no clearance for screw heads, soooo

    3D printing to the rescue:

    Boost converter - installed
    Boost converter – installed

    Now that the hulking ET227 operates in saturation mode, I removed the blower to make room for the power supply. Two strips of double-stick foam tape fasten the holder to the removable tray inside the Dell GX270’s case.

    It’s basically a rounded slab with recesses for the PCB and clearance for solder-side components:

    Boost converter mount - as printed
    Boost converter mount – as printed

    The solid model shows the screw holes sitting just about tangent to the PCB recess:

    XW029 Booster PCB Mount
    XW029 Booster PCB Mount

    That’s using the new OpenSCAD with length scales along each axis; they won’t quite replace my layout grid over the XY plane, but they certainly don’t require as much computation.

    I knew my lifetime supply of self-tapping hex head 4-40 screws would come in handy for something:

    Boost converter in mount
    Boost converter in mount

    The program needs to know the PCB dimensions and how much clearance you want for the stuff hanging off the bottom:

    PCBoard = [66,35,IntegerMultiple(1.8,ThreadThick)];
    
    BottomParts = [[1.5,-1.0,0,0],	// xyz offset of part envelope
    				[60.0,37.0,IntegerMultiple(3.0,ThreadThick)]];	// xyz envelope size (z should be generous)
    

    That’s good enough for my simple needs.

    The hole locations form a list-of-vectors that the code iterates through:

    Holes = [			// PCB mounting screw holes: XY + rotation
    		[Margin - ScrewOffset,MountBase[Y]/2,180/6],
    		[MountBase[X] - Margin + ScrewOffset/sqrt(2),MountBase[Y] - Margin + ScrewOffset/sqrt(2),15],
    		[MountBase[X] - Margin + ScrewOffset/sqrt(2),Margin - ScrewOffset/sqrt(2),-15],
    		];
    
    ... snippage ...
    
    for (h = Holes) {
    	translate([h[X],h[Y],-Protrusion]) rotate(h[Z])
    		PolyCyl(Tap4_40,MountBase[Z] + 2*Protrusion,6);
    }
    

    That’s the first occasion I’ve had to try iterating a list and It Just Worked; I must break the index habit. The newest OpenSCAD version has Python-ish list comprehensions which ought to come in handy for something.

    The “Z coordinate” of each hole position gives its rotation, so I could snuggle them up a bit closer to the edge by forcing the proper polygon orientation. The square roots in the second two holes make them tangent to the corners of the PCB, rather than the sides, which wasn’t true for the first picture. Fortunately, the washer head of those screws turned out to be just big enough to capture the PCB anyway.

    The OpenSCAD source code:

    // PCB mounting bracket for XW029 DC-DC booster
    // Ed Nisley - KE4ZNU - January 2015
    
    Layout = "Build";			// PCB Block Mount Build
    
    //- Extrusion parameters must match reality!
    //  Print with 4 shells and 3 solid layers
    
    ThreadThick = 0.20;
    ThreadWidth = 0.40;
    
    HoleWindage = 0.2;			// extra clearance
    
    Protrusion = 0.1;			// make holes end cleanly
    
    AlignPinOD = 1.70;			// assembly alignment pins: filament dia
    
    function IntegerMultiple(Size,Unit) = Unit * ceil(Size / Unit);
    
    X = 0;						// useful subscripts
    Y = 1;
    Z = 2;
    
    //----------------------
    // Dimensions
    
    inch = 25.4;
    
    Tap4_40 = 0.089 * inch;
    Clear4_40 = 0.110 * inch;
    Head4_40 = 0.211 * inch;
    Head4_40Thick = 0.065 * inch;
    Nut4_40Dia = 0.228 * inch;
    Nut4_40Thick = 0.086 * inch;
    Washer4_40OD = 0.270 * inch;
    Washer4_40ID = 0.123 * inch;
    
    PCBoard = [66,35,IntegerMultiple(1.8,ThreadThick)];
    
    BottomParts = [[1.5,-1.0,0,0],				// xyz offset of part envelope
    				[60.0,37.0,IntegerMultiple(3.0,ThreadThick)]];			// xyz envelope size (z should be generous)
    
    Margin = IntegerMultiple(Washer4_40OD,ThreadWidth);
    
    MountBase = [PCBoard[X] + 2*Margin,
    			PCBoard[Y] + 2*Margin,
    			IntegerMultiple(5.0,ThreadThick) + PCBoard[Z] + BottomParts[1][Z]
    			];
    echo("Mount base: ",MountBase);
    
    ScrewOffset = Clear4_40/2;
    
    Holes = [									// PCB mounting screw holes: XY + rotation
    		[Margin - ScrewOffset,MountBase[Y]/2,180/6],
    		[MountBase[X] - Margin + ScrewOffset/sqrt(2),MountBase[Y] - Margin + ScrewOffset/sqrt(2),15],
    		[MountBase[X] - Margin + ScrewOffset/sqrt(2),Margin - ScrewOffset/sqrt(2),-15],
    		];
    
    CornerRadius = Washer4_40OD / 2;
    
    //----------------------
    // 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);
    }
    
    module ShowPegGrid(Space = 10.0,Size = 1.0) {
    
      RangeX = floor(100 / Space);
      RangeY = floor(125 / Space);
    
    	for (x=[-RangeX:RangeX])
    	  for (y=[-RangeY:RangeY])
    		translate([x*Space,y*Space,Size/2])
    		  %cube(Size,center=true);
    
    }
    
    //----------------------
    // Build things
    
    module PCB() {
    
    	union() {
    		cube(PCBoard);
    		translate(BottomParts[X] - [0,0,BottomParts[1][Z]])
    			cube(BottomParts[Y] + [0,0,Protrusion]);
    	}
    
    }
    
    module Block() {
    	translate([MountBase[X]/2,MountBase[Y]/2,0])
    		hull()
    			for (i = [-1,1], j = [-1,1])
    				translate([i*(MountBase[X]/2 - CornerRadius),j*(MountBase[Y]/2 - CornerRadius)],0)
    					cylinder(r=CornerRadius,h=MountBase[Z] - Protrusion,$fn=8*4);
    }
    
    module Mount() {
    
    	difference() {
    		Block();
    
    		translate([MountBase[X]/2 - PCBoard[X]/2 + BottomParts[0][X] - Protrusion,
    					-MountBase[Y]/2,
    					MountBase[Z] - PCBoard[Z] - BottomParts[1][Z]])
    			cube([BottomParts[1][X] + 2*Protrusion,
    					2*MountBase[Y],
    					2*BottomParts[1][Z]]);
    
    		translate([MountBase[X]/2 - PCBoard[X]/2,		// PCB recess
    					MountBase[Y]/2 - PCBoard[Y]/2,
    					MountBase[Z] - PCBoard[Z]])
    			PCB();
    		for (h = Holes) {
    			translate([h[X],h[Y],-Protrusion]) rotate(h[Z])
    				PolyCyl(Tap4_40,MountBase[Z] + 2*Protrusion,6);
    		}
    	}
    
    }
    
    //ShowPegGrid();
    
    if (Layout == "PCB")
    	PCB();
    
    if (Layout == "Block")
    	Block();
    
    if (Layout == "Mount")
    	Mount();
    
    if (Layout == "Build")
    	translate([-MountBase[X]/2,-MountBase[Y]/2,0])
    	Mount();
    
  • Last of the Energizer CR2032 Cells

    All three Energizer CR2032 lithium cells installed at the end of November failed in December, with this being the most dramatic example:

    Attic - Insulated Box - Early battery failure
    Attic – Insulated Box – Early battery failure

    Now, granted, it was mighty chilly in the attic, but failing after 18 hours seems unreasonable. So much for last month’s data.

    I’ve started a batch of Maxell cells with the more reasonable date code 3O, which seems to indicate a manufacturing date of 2013 October.

    We shall see…