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 158: Bobbin Winder Tires

    The bobbin winder atop the Kenmore 158 sewing machine has a rubber tire that contacts a ribbed ring on the inside surface of the handwheel; the clutch knob disengages the main shaft and you run the motor at top speed. As you’d expect, both age and wear take their toll on the rubber, to the extent that the winder on Mary’s machine stopped turning. I swapped it for the slightly less decrepit winder on the Crash Test Dummy, but that was obviously a stop-gap measure.

    I mistakenly thought the metal wheel consisted of two plates that clamped a rubber disk in place, with no possibility of removal:

    Bobbin Winder - old tire
    Bobbin Winder – old tire

    The fact that the spare parts list didn’t include the rubber disk helped convince me.

    Eventually, I stumbled over replacement “tires” on, of course, eBay that suggested how to dismount them:

    Bobbin Winder - wheel and tires
    Bobbin Winder – wheel and tires

    Yup, that sucker slides right off.

    Anyhow, the replacements seem to be standard industrial O-rings, rather than the original tire with a flattened rim:

    Bobbin Winder - old vs new tire
    Bobbin Winder – old vs new tire

    The new tires measure 28.94 mm OD on the bench (I don’t trust that last digit, either) and 29.56 mm OD installed. The (hardened and cracked) old tires measure 29.94, 30.06, and 30.28 mm OD on the bench; that’s a radius anywhere from 0.2 mm to 0.4 mm larger. The winder’s mounting screws provide a very small adjustment range that helps a bit.

    Knowing that I needed an O-ring, I checked the assortment of “standard size” O-rings I bought many, many years ago, which once again failed to offer up anything suitable. To the best of my knowledge, that kit has never had the right size; apparently, every application uses a different standard.

    The O-ring definitely puts less rubber on the handwheel than the tire, but seems to drive the bobbin winder well enough to fill a handful of bobbins without any of the previous drama.

  • End and Beginning of the Computer Glasses

    Having repaired these once before, I wasn’t too surprised when this happened:

    Eyeglasses - broken nose bridge wire
    Eyeglasses – broken nose bridge wire

    Evidently the “titanium” has fatigued, because the repair lasted barely nine months.

    Rather than try to fix them again, I sent my new prescriptions halfway around the planet and, a bit under two weeks later, had three glasses: normal, computer, and sun. This time, I went with 38 mm tall lenses, a heavier nose bridge, and traditional aviator sunglasses.

    For the record, the regular prescription was:

    Normal prescription - 2014-12

    Tweaking that by +0.75 diopter on the sphere puts my far point focus on the monitors across the desk and backing -0.75 diopter from the adder keeps the same near-point reading correction:

    Computer prescription - 2014-12
    Computer prescription – 2014-12

    They’re all no-line progressive bifocals made from 1.57 high-index plastic with anti-reflection coating, for a grand total of $135 delivered.

    That being only slightly more than the estimated cost of fixing one broken Silhouette frame temple, Mary will try living in the future, too.

  • Kenmore 158: Normalized Pedal Position

    Adjusting the output voltage vs. position for the sewing machine’s food pedal quickly revealed that the code shouldn’t depend on the actual ADC values. That’s blindingly obvious in hindsight, of course.

    The maximum with the pedal in its overtravel region doesn’t change by much, because the Hall effect sensor output voltage saturates in a high magnetic field. I used a hardcoded word PedalMax = 870; which comes from 4.25 V at the ADC input.

    On the low end, the sensor output can change by a few counts depending on small position changes, so I sampled the (presumably released) pedal output during the power-on reset:

    	PedalMin = ReadAI(PIN_PEDAL);				// set minimum pedal input value
    	printf("Set PedalMin: %u\r\n",PedalMin);
    	PedalMaxClamp = 100;						// set upper speed limit
    
    

    Given the complete ADC range, this function normalizes a value to the range [0,100], conveniently converting the pedal position into a percent of full scale:

    int PedalPercent(word RawPos) {
    int Clamped;
    
    	Clamped = constrain(RawPos,PedalMin,PedalMax);
    	return map(Clamped,PedalMin,PedalMax,0,100);
    }
    

    Graphing the normalized values against pedal position would have the same shape as the ADC values. All I’m doing is rescaling the Y axis to match the actual input limits.

    The top of the main loop captures the pedal position:

    PedalADC = ReadAI(PIN_PEDAL);
    PedalPosition = PedalPercent(PedalADC);
    

    Now, it’s easy to add a slight deadband that ensures the sewing machine doesn’t start when you give the pedal a harsh look; the deadband is now a percent of full travel, rather than a hard-coded ADC count or voltage.

    For example, in needle-follows-pedal mode, you must press the pedal by more than 10% to start the stitch, slightly release it to finish the stitch, and then almost completely release it to proceed to the next stitch:

    	case PD_FOLLOW:
    		if (PedalPosition > 10) {
    			printf("Pedal Follow\r\n");
    			ParkNeedle(NS_DOWN);
    			do {
    				PedalPosition = PedalPercent(ReadAI(PIN_PEDAL));
    			} while (PedalPosition > 10);
    			ParkNeedle(NS_UP);
    			do {
    				PedalPosition = PedalPercent(ReadAI(PIN_PEDAL));
    			} while (PedalPosition > 2);
    		}
    		break;
    

    Adjusting percentages turns out to be much easier than fiddling with ADC values.

    Obvious, huh?

  • Kenmore 158: Pedal Recalibration

    With the Crash Test Dummy in place, Mary reports that the pedal required too much motion to reach the full speed position. The graph from the last time around shows the output as a function of pedal position:

    Hall sensor output vs pedal depression
    Hall sensor output vs pedal depression

    I’d done some fiddling around after recording that data, but it remained pretty close to the truth.

    A new scale, not quite in the same spot as the previous one:

    Kenmore 158 - Foot Pedal - motion recalibration
    Kenmore 158 – Foot Pedal – motion recalibration

    The two long lines mark the active region after I finished the mechanical tweaking described below; the pedal now produces nearly full output just beyond the 12 mm mark and has about that much overtravel. Measuring those values requires squeezing the pedal by hand, holding its position, and recording the ADC output dumped by the motor control program in the Arduino, a process that could be improved, but to not much benefit.

    The original pedal writeup goes into the gory details, but the bottom line is that the mechanical motion producing that output range depends on the length of the rod from the actuator bar to the magnet.

    The original version had a thin nut securing a screw inside the brass shaft to the actuator:

    Kenmore 158 - Hall speed control - prototype interior
    Kenmore 158 – Hall speed control – prototype interior

    I later swapped the nut for three washers, after observing that the nut wasn’t actually necessary, but that produced too much dead travel at the beginning of motion.

    We discovered that the actuator bar could slip off the end of the ramp cast into the pedal cover, jamming the end of the ramp, making the case extremely difficult to disassemble, and causing heartache & confusion. Affixing a pair of rubber feet to the rear wall of the pedal case with tapeless sticky keeps the bar about half a millimeter further forward and eliminates that problem:

    Kenmore 158 - foot pedal - short actuation
    Kenmore 158 – foot pedal – short actuation

    A second nut moved the brass rod forward, but that turned out to be a bit too much, so it now has a single, slightly thicker, nut. The 3D printed frame allows for far more travel in each direction than is strictly necessary, specifically to allow this fine tuning; it’s possible to make the rod’s resting position too close to the Hall effect sensor and have them collide at full travel, but I managed to avoid that.

    It’s impossible to measure anything with the case disassembled: each change requires reassembling everything, measuring, and iterating.

    After some of this & that, this graph shows the final output curve, with the Y axis in raw ADC counts at 100/div:

    Foot Pedal - ADC vs position - graph detail
    Foot Pedal – ADC vs position – graph detail

    The first 3 mm doesn’t produce much change in the output, it smoothly changes to the more-or-less linear ramp up to 12 mm, then tapers off to full output beyond 14 mm. That’s pretty close to the original graph, with full throttle occurring a bit more than 2 mm earlier; the difference between the two scales may have some effect. In any event, Mary reports it feels much better.

    Trust me: that matters.

    The original data from the first and second mods, plus a teeny version of that graph:

    Foot Pedal - ADC vs position
    Foot Pedal – ADC vs position
  • Kenmore 158: Crash Test Dummy Installation

    The Crash Test Dummy’s double-walled and somewhat crushed base turns out to be slightly larger front-to-back than the one on Mary’s original Kenmore 158 (which has a later serial number), but it still fits into the cutout in the insulating board we’re using in lieu of a Real Sewing Surface:

    Kenmore 158 Controller - First Quilting
    Kenmore 158 Controller – First Quilting

    Lashing the UI data/power cable to the motor & LED / sensor cables from the sewing machine keeps the Arduino Mega + TFT from falling off the tabletop, but the arrangement reeks of impromptu.

    Early reports indicate that the pedal doesn’t feel quite right, with faster speeds requiring too much travel. Given that I worked hard to get more travel with slower transitions into that thing, differences shouldn’t come as any surprise, but … this will require some tweaking.

  • Kenmore 158 Base: Cable Port

    The Crash Test Dummy machine arrived from the usual eBay seller in a cardboard box with a few rigid foam strips and some closed-cell foam sheets tossed inside. The seller thought the machine was “adequately protected”, which turned out to be, at best, optimistic:

    Kenmore 158 - Crash Test Dummy Case
    Kenmore 158 – Crash Test Dummy Case

    Fortunately, the crushed case protected the sewing machine itself and, given that I specifically bought it with the intent of making mistakes thereupon, it worked well enough. At one point, it vibrated off a desk, landed face-down on the concrete basement floor, and now the stitch selection / length cam followers don’t follow their cams very well at all.

    I modified the cracked-but-workable base to pass the connectors on the AC motor power and LED power / position sensor cables:

    Kenmore 158 Base - cable hole
    Kenmore 158 Base – cable hole

    That was done by chucking a hole saw in the drill press, running at lowest speed, resting the other end of the case on my thigh, and tipping my foot to drive the case upward into the saw. Worked surprisingly well, but I’d appreciate it if you didn’t use that technique yourself.

    Now that the Crash Test Dummy resembles a sewing machine again, running a few trial stitches in scrap fabric showed that it works well enough for straight-line sewing and free-motion quilting:

    Kenmore 158 Crash Test Dummy - test stitching
    Kenmore 158 Crash Test Dummy – test stitching

    We installed it in the Quilting Room, ready for a more extensive evaluation on an actual quilt…

  • Kenmore 158 UI: Button Commands

    The button definition table now includes a string that becomes the button’s command to the sewing machine motor controller:

    #define MAXCMDLEN 2
    
    enum bstatus_t {BT_DISABLED,BT_UP,BT_DOWN};
    
    typedef void (*pBtnFn)(byte BID);		// button action function called when hit
    
    struct button_t {
    	byte ID;					// button identifier, 0 unused
    	byte Group;					// radio button group, 0 for none
    	byte Status;				// button status
    	word ulX,ulY;				// origin: upper left
    	word szX,szY;				// button image size
    	pBtnFn pAction;				// button function
    	char Cmd[MAXCMDLEN + 1];	// command string
    	char NameStem[9];			// button BMP file name - stem only
    };
    
    struct button_t Buttons[] = {
    	{ 1,	1, BT_UP,		  0,0,		 80,80,	DefaultAction,	"Nu",	"NdUp"},
    	{ 2,	1, BT_UP,		  0,80,		 80,80,	DefaultAction,	"Na",	"NdAny"},
    	{ 3,	1, BT_DOWN,		  0,160,	 80,80,	DefaultAction,	"Nd",	"NdDn"},
    
    	{ 4,	2, BT_DOWN,		 80,0,		120,80,	DefaultAction,	"Pr",	"PdRun"},
    	{ 5,	2, BT_UP,		 80,80,		120,80,	DefaultAction,	"P1",	"PdOne"},
    	{ 6,	2, BT_UP,		 80,160,	120,80,	DefaultAction,	"Pf",	"PdFol"},
    
    	{ 7,	3, BT_DOWN,		200,0,		 80,80,	DefaultAction,	"Sh",	"SpMax"},
    	{ 8,	3, BT_UP,		200,80,		 80,80,	DefaultAction,	"Sm",	"SpMed"},
    	{ 9,	3, BT_UP,		200,160,	 80,80,	DefaultAction,	"Sl",	"SpLow"},
    
    //	{10,	0, BT_UP,	311,0,		  8,8,	CountColor,	'\0',	'\0',	"Res"}
    };
    
    byte NumButtons = sizeof(Buttons) / sizeof(struct button_t);
    

    The default button handler now sends the button’s command string whenever it finds the button down after all the processing:

    #define TRACEACTION false
    
    void DefaultAction(byte BID) {
    
    byte i,BX;
    byte Group;
    
    	if (!BID) {											// not a valid ID
    		printf("** Button ID zero in DefaultAction\r\n");
    		return;
    	}
    
    	BX = FindButtonIndex(BID);
    	if (BX == NumButtons) {								// no button for that ID
    //		printf("** No table entry for ID: %d\r\n",BID);
    		return;
    	}
    
    #if TRACEACTION
    	printf("Default action: BID %d St %d -- ",BID,Buttons[BX].Status);
    #endif
    
    	if (Buttons[BX].Status == BT_DISABLED) {		// cannot do anything to disabled buttons
    #if TRACEACTION
    		printf("disabled\r\n");
    #endif
    		return;
    	}
    
    	Group = Buttons[BX].Group;
    
    	if (Group) {									// member of group?
    		if (Buttons[BX].Status == BT_DOWN) {		//  if down, remain that way
    #if TRACEACTION
    			printf("already down\r\n");
    #endif
    		}
    		else {										//  is up
    			for (i=0; i<NumButtons; i++) {			//   so unpush other buttons in group
    				if ((Buttons[i].Group == Group) && (Buttons[i].Status == BT_DOWN) && (i != BX)) {
    #if TRACEACTION
    					printf("release ID %d - ",Buttons[i].ID);
    #endif
    					Buttons[i].Status = BT_UP;
    					DrawButton(Buttons[i].ID,Buttons[i].Status);
    				}
    			}
    #if TRACEACTION
    			printf("push\r\n");
    #endif
    			Buttons[BX].Status = BT_DOWN;			//   and push this button down
    		}
    	}
    	else {											// not a group, so just toggle
    #if TRACEACTION
    		printf("toggle\r\n");
    #endif
    		Buttons[BX].Status = (Buttons[BX].Status == BT_DOWN) ? BT_UP : BT_DOWN;
    	}
    
    	DrawButton(BID,Buttons[BX].Status);
    
    	if (Buttons[BX].Status == BT_DOWN) {			// is this button now (or still) pressed?
    		SendCmd(Buttons[BX].Cmd);
    	}
    
    }
    

    That means the controller will see identical commands each time the button gets pressed, which doesn’t have any downsides. You could build an increment / decrement speed function without much trouble, although there’s still no way to display any returned values on the LCD.

    Working under the possibly unwarranted assumption that serial communications between the two Arduinos won’t encounter any errors, I just wrap the command string in a distinctive marker and send it off:

    void SendCmd(char *pCmd) {
    
    char Msg[MAXCMDLEN + 3];
    
    	strcpy(Msg,"[");
    	strcat(Msg,pCmd);
    	strcat(Msg,"]");
    
    	Serial.print("Cmd: ");							// copy to console
    	Serial.println(Msg);
    
    	Serial1.println(Msg);							// send command!
    
    }
    

    The Serial1 port runs at a nose-pickin’ 9600 baud, because the motor controller often gets wrapped up in what it’s doing. On the other paw, when the controller gets distracted, the operator will be feeding fabric past the needle at a pretty good clip and won’t have a finger to spare for the UI buttons, so it would probably work no matter what.

    That mismatch, however, allows the motor controller to babble on at length, without overruning the UI’s console output. This routine collects lines from the controller:

    char GetStatLine(void) {
    
    static byte Index = 0;
    char NewChar;
    
    	if (!Serial1.available()) {					// return if no chars in queue
    		return 0;
    	}
    
    	do {
    		NewChar = Serial1.read();
    		switch (NewChar) {
    		case '\r':								// end-of-line on CR
    			MCtlBuffer[Index] = 0;
    			Index = 0;
    			return strlen(MCtlBuffer);			// return from mid-loop
    			break;								//  unnecessary
    		case '\n':								// discard NL
    			break;
    		default:
    			MCtlBuffer[Index] = NewChar;		// store all others
    			Index += (Index < STATMAXLEN) ? 1 : 0;
    		}
    	} while (Serial1.available());
    
    	return 0;
    
    }
    

    A call in the main loop dumps each line after the terminating CR:

    char GetStatLine(void) {
    
    static byte Index = 0;
    char NewChar;
    
    	if (!Serial1.available()) {					// return if no chars in queue
    		return 0;
    	}
    
    	do {
    		NewChar = Serial1.read();
    		switch (NewChar) {
    		case '\r':								// end-of-line on CR
    			MCtlBuffer[Index] = 0;
    			Index = 0;
    			return strlen(MCtlBuffer);			// return from mid-loop
    			break;								//  unnecessary
    		case '\n':								// discard NL
    			break;
    		default:
    			MCtlBuffer[Index] = NewChar;		// store all others
    			Index += (Index < STATMAXLEN) ? 1 : 0;
    		}
    	} while (Serial1.available());
    
    	return 0;
    
    }
    

    Which produces output like this:

    Kenmore Model 158 User Interface
     Compiled: Jan 26 2015 at 15:33:52
    Ed Nisley - KE4ZNU
    TS... OK
    SD... OK
    LCD... should be active
    Cmd: [Nd]
    Cmd: [Pr]
    Cmd: [Sh]
    MC |** Bad command string: [--]
    MC |  540, 65535,   194
    MC |  610,     0,   194
    MC |  783,    55,   236
    MC | 1262,    84,   391
    MC | 1452,   116,   394
    MC | 1633,   123,   394
    MC | 1494,   132,   405
    MC | 1768,   126,   406
    MC | 1488,   126,   406
    MC | 1425,   137,   406
    MC | 1517,   132,   406
    MC | 1461,   126,   209
    MC |Coast: 1099
    MC |Parking Stop down: Done
    MC | stopped
    

    The “bad command string” isn’t actually an error. The first outbound line consists of [--] and a carriage return, which isn’t a valid command, just to make sure that the motor controller’s incoming serial port buffer doesn’t contain any junk. Obviously, I should add that string to the command decoder…