Ed Nisley's Blog: Shop notes, electronics, firmware, machinery, 3D printing, laser cuttery, and curiosities. Contents: 100% human thinking, 0% AI slop.
Large quilting projects require lots of thread, beyond the capacity of the previous spool adapter, so we came up with a different solution:
Large spool holder
These are cheap & readily available from the usual sources, but recent reviews indicate that the “metal” base has become plastic and the build quality isn’t anything to rejoice over. My feeling is that if it’s going to become a shop project anyway, I should just conjure something suitable from the heap.
The base is a random plastic project box that came with a flimsy sheet-steel top, which I replaced with a rectangle of 0.1 inch = 2.5 mm aluminum plate for more heft. The box is filled with 1.5 pounds of wheel weights, so it’s not going anywhere on its own. The silicone rubber feet probably don’t add much to the project, but why not use ’em?
The feed hook started life as copper-flashed welding filler rod, smooth to the thread and pleasant to the eye, sitting in a hole drilled into a stainless steel 10-32 screw. It’s long enough to feed the thread just above the Kenmore’s top surface. A hook works better than an eyelet: just pass the thread over the hook and you’re done.
The central shaft is a wood dowel, shaped & sanded on the (metal) lathe, held in place by another 10-32 screw. Inside the spool sits a length of “3/4 inch” CPVC pipe (ID = 0.7 inch, OD = 0.875 inch, gotta love those plumbing measurements) that’s a sloppy fit in the just-over 1 inch spool ID.
The smaller spools fit directly on the dowel, perhaps atop the CPVC sleeve.
This seems to work OK, but I’m going to trim the dowel down to just over the length of the spool, so the thread will feed without touching the wood. I thought stacking the smaller spools atop the CPVC sleeve made sense, but that turned out to not be the case.
Took about an hour to conjure with found materials and without a hint of 3D printing…
The user community asked for toned-down buttons, in place of my rather garish color scheme. A bit of twiddling with the Hue parameter produced these buttons:
Kenmore 158 UI – Pastel Buttons
Which look pretty good in context:
Kenmore 158 UI – Pastel buttons
The Bash script, which includes Unicode characters that may confuse your browser:
The trick depends on specifying the colors with HSB, rather than RGB, so that the buttons in each row have the same hue and differ in saturation and brightness. The Imagemagick incantations look like this:
Disabled: hsb\(${HUE}%,50%,40%\)
Unselected: hsb\(${HUE}%,100%,70%\)
Selected: hsb\(${HUE}%,100%,100%\)
For whatever reason, the hue must be a percentage if the other parameters are also percentages. At least, I couldn’t figure out how to make a plain integer without a percent sign suffix work as a degree value for hue.
Anyhow, in real life they look pretty good and make the selected buttons much more obvious:
The LCD screen looks just like that; I blew out the contrast on the surroundings to provide some context. The green square on the left is the Arduino Mega’s power LED, the purple dot on the right is the heartbeat spot.
The new “needle stop anywhere” symbol (left middle) is the White Draughts Man Unicode character: ⛀ = U+26C0. We call them checkers here in the US, but it’s supposed to look like a bobbin, as you must disengage the handwheel clutch and stop the main shaft when filling a bobbin; the needle positioning code depends on the shaft position sensor.
Weirdly, Unicode has no glyphs for sewing, not even a spool of thread, although “Fish Cake With Swirl” (🍥 = U+1F365) came close. Your browser must have access to a font with deep Unicode support in order to see that one…
For reasons which are, trust me on this, not relevant here, we now have a third Kenmore 158 sewing machine: a freebie that sat under a roof leak in an unused room some years ago and wasn’t cleaned before being stored. Even though not much water got inside the covers, the bobbin winder shaft froze solid.
Two black screws hold it to the cover and provide a slight adjustment of the tire-to-handwheel distance:
Bobbin Winder – old tire
Prior to this adventure, I soaked the shaft in penetrating oil for a week or two, but to no avail.
I didn’t take any before-the-repair photos, but it looked like this afterward, with the new tire installed…
From the top right (looking over the handwheel):
Bobbin Winder – assembled – top right
Notice the small rectangular hole just below the larger section of the shaft in the protruding part of the pot metal housing. That’s supposed to be an oil hole, but it’s also a fine water inlet.
From the top left:
Bobbin Winder – assembled – top left
The two obvious screws remove the obvious parts, but beware the compression spring:
Bobbin Winder – fill sense lever
And the torsion spring:
Bobbin Winder – drive latch
Some experimentation with a strap wrench rotated the wheel on the (still firmly frozen) shaft, which suggested the joint was a press fit without a setscrew, splines, or adhesive.
Grabbing the shaft lightly in a machinist’s vise, resting it atop the bench vise, and giving it a few shots with a drift punch drove it downward through the housing:
Bobbin Winder – driving out spindle
More gentle beating produced this heartrending scene:
Bobbin Winder – corroded shaft
Water just isn’t any good at all for unlubricated steel in a pot-metal bushing…
Anyhow, the shaft & housing cleaned up well, although they look a tad grody, and everything went back together in the reverse order.
I added a drop of light oil through the lube port, chucked the shaft in the drill press, spun it for a minute at low speed to wear off a slight binding, and it’s all good again.
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
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
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
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.
case PD_RUN :
if (PedalPosition > 5) {
if (MotorDrive.State != RUNNING) {
EnableMotor();
MotorDrive.State = RUNNING;
}
BaseDAC.setVoltage(0x0fff,false); // give it a solid pulse
SampleCurrent(PIN_CURRENT_SENSE); // sample over a half cycle
if (DriveOnTime > CurrentSamplingTime) {
delay(DriveOnTime - CurrentSamplingTime);
}
// Pedal to the metal?
// ... if so, stall here with motor drive fully on until the pedal releases
while ((MotorDrive.SpeedRange == SPEED_HIGH) && (PedalPosition >= 100)) {
PedalPosition = PedalPercent(ReadAI(PIN_PEDAL));
}
BaseDAC.setVoltage(0,false); // ... then turn it off
delay(map(constrain(PedalPosition,0,PedalMaxClamp),
0,100,
DriveOffTime,0));
}
else {
if (MotorDrive.State == RUNNING) {
if (MotorSensor.RPM) {
printf("Coast: %d\r\n",MotorSensor.RPM);
delay(100);
}
else {
printf("Parking ");
ParkNeedle(MotorDrive.ParkPosition);
MotorDrive.State = STOPPED;
printf(" stopped\r\n");
}
}
}
break;
The magic happens in highlighted statement, which flips the sense of the pedal motion and linearly scales the result into a delay() value ranging from 120 ms (pedal barely pressed) down to 0 ms (pedal almost fully pressed). If the pedal is all the way down, then the preceding while() locks up until it’s released a bit, whereafter the delay will be nearly zero.
That sorta-kinda worked, but the user community said that the pedal response required pushing too hard for top speed: it should get faster, sooner. The problem came from the simplistic way I set the speed: it was inversely proportional to the position.
Plotting speed against pedal position shows the problem:
Speed vs pedal – period control
I figured the right approach was to make the speed vary linearly with the pedal position, so the trick was to plot the off-time delay vs. the actual speed:
Off-time delay vs speed – period control
The second-order equation bottles up a bunch of nonlinear things. Given that this was using the original code, I made the dubious assumption that more-or-less the same delay in the new code would produce more-or-less the same speed.
The new code discards the current-sampling routine that I was using to get a fixed delay (because I don’t need to know the current in pulse-drive mode), then used that time for the floating-point calculation required to find the off-time delay. That chunk of code took a bit of fiddling to get right:
case PD_RUN :
if (PedalPosition > 5) {
if (MotorDrive.State != RUNNING) {
EnableMotor();
MotorDrive.State = RUNNING;
}
BaseDAC.setVoltage(0x0fff,false); // give it a solid pulse
MillisOn = millis() + (unsigned long)DriveOnTime;
TargetSpeed = (float)map(constrain(PedalPosition,0,PedalMaxClamp),
0,100,
0,700); // quick and dirty 0 to 700 stitch/min range
OffTime = (int)((1.94e-4*TargetSpeed - 0.286)*TargetSpeed + 106.0);
OffTime = constrain(OffTime,0,120); // clamp to reasonable range
MillisOff = MillisOn + (unsigned long)OffTime; // compute end of off time
while (millis() <= MillisOn) { // wait for end of pulse
continue;
}
if ((PedalPosition >= 100) && (MotorDrive.SpeedRange == SPEED_HIGH)) { // pedal down in full speed mode?
printf("Full speed ... ");
OffTime = 0;
while (PedalPosition >= 100) { // ... full throttle!
PedalPosition = PedalPercent(ReadAI(PIN_PEDAL));
}
BaseDAC.setVoltage(0,false); // pedal released, start coasting
printf(" done\r\n");
}
else { // pedal partially pressed
BaseDAC.setVoltage(0,false); // pulse done, turn motor off
while (millis() <= MillisOff) { // wait for end of off period
continue;
}
}
}
But the result looks as pretty as you could possibly expect:
Speed vs pedal – linearized speed control
The pedal still provides a soft-start transition from not moving to minimum speed, which remains an absolute requirement: having an abrupt transition to that straight line would be a Bad Thing. Fortunately, the nature of the magnet moving toward the Hall effect sensor gives you that for free.
Although we’re still evaluating the results, the user community seems happier…
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:
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.