Search Results for: kenmore arduino

Kenmore 158 UI: Automatic Button Builder

Given the glacially slow Arduino touch-screen TFT display as a first pass UI for the Kenmore 158 sewing machine, I need some UI elements.

I need buttons. Lots of buttons.

Each button will have several different states that must be visually distinct:

  • Disabled – not available for pressing
  • Released – can be pressed and is inactive
  • Pressed – has been pressed and is now active

There may be other states, but those should be enough to get started.

I’d rather not draw that detail by hand for each button, so some tinkering with the Bash script driving the Imagemagick routines produced these results:



Aren’t those just the ugliest buttons you’ve ever seen?

The garish colors identify different functions, the crude shading does a (rather poor) job of identifying the states, and the text & glyphs should be unambiguous in context. Obviously, there’s room for improvement.

The point is that I can begin building the UI code that will slap those bitmaps on the Arduino’s touch-panel LCD while responding to touches, then come back and prettify the buttons as needed. With a bit of attention to detail, I should be able to re-skin the entire UI without building the data into the Arduino sketch, but I’ll start crude.

The script that defines the button characteristics and calls the generator script:

./ NdDn springgreen4 ⤓
./ NdUp springgreen4 ⤒
./ NdAny springgreen4 ⟳ 80 80 40
./ PdOne sienna One 120 80
./ PdFol sienna Follow 120 80
./ PdRun sienna Run 120 80
./ SpMax maroon1  🏃 80 80 40
./ SpMed maroon2  🐇 80 80 40
./ SpLow maroon3  🐌
montage *bmp -tile 3x -geometry +2+2 Buttons.png
display Buttons.png

As before, if you don’t see rabbit and snail glyphs, then your fonts don’t cover those Unicode blocks.

The quick-and-dirty script that produces three related buttons for each set of parameters:

# create family of simple beveled buttons
# Ed Nisley - KE4ZNU
# January 2015

[ -z $1 ] && FN=Test || FN=$1
[ -z $2 ] && CLR=red || CLR=$2
[ -z $3 ] && TXT=x   || TXT=$3
[ -z $4 ] && SX=80   || SX=$4
[ -z $5 ] && SY=80   || SY=$5
[ -z $6 ] && PT=25   || PT=$6
[ -z $7 ] && BDR=10  || BDR=$7

echo fn=$FN clr=$CLR txt=$TXT sx=$SX sy=$SY pt=$PT bdr=$BDR

echo Working ...

echo Shape
convert -size ${SX}x${SY} xc:none \
-fill $CLR -draw "roundrectangle $BDR,$BDR $((SX-BDR)),$((SY-BDR)) $((BDR-2)),$((BDR-2))" \

echo Highlights
convert ${FN}_s.png \
  \( +clone -alpha extract -blur 0x12 -shade 110x2 \
  -normalize -sigmoidal-contrast 16,60% -evaluate multiply .5\
  -roll +4+8 +clone -compose Screen -composite \) \
  -compose In  -composite \

convert ${FN}_s.png \
  \( +clone -alpha extract -blur 0x12 -shade 110x0 \
  -normalize -sigmoidal-contrast 16,60% -evaluate multiply .5\
  -roll +4+8 +clone -flip -flop -compose Screen -composite \) \
  -compose In  -composite \

echo Borders
convert ${FN}_h.png \
  \( +clone -alpha extract  -blur 0x2 -shade 0x90 -normalize \
  -blur 0x2  +level 60,100%  -alpha On \) \
  -compose Multiply -composite \

convert ${FN}_l.png \
  \( +clone -alpha extract  -blur 0x2 -shade 0x90 -normalize \
  -blur 0x2  +level 60,100%  -alpha On \) \
  -compose Multiply -composite \

echo Buttons
convert ${FN}_s.png \
  -font /usr/share/fonts/custom/Symbola.ttf  -pointsize ${PT}  -fill black  -stroke black \
  -gravity Center  -annotate 0 "${TXT}"  -trim -repage 0x0+7+7 \
  \( +clone -background navy -shadow 80x4+4+4 \) +swap \
  -background snow4  -flatten \

convert ${FN}_bl.png \
  -font /usr/share/fonts/custom/Symbola.ttf  -pointsize ${PT}  -fill black  -stroke black \
  -gravity Center  -annotate 0 "${TXT}"  -trim -repage 0x0+7+7 \
  \( +clone -background navy -shadow 80x4+4+4 -flip -flop \) +swap \
  -background snow4  -flatten \

convert ${FN}_bh.png \
  -font /usr/share/fonts/custom/Symbola.ttf  -pointsize $PT  -fill black  -stroke black \
  -gravity Center  -annotate 0 "${TXT}"  -trim -repage 0x0+7+7 \
  \( +clone -background navy -shadow 80x4+4+4 \) +swap \
  -background snow4  -flatten \

echo BMPs
for ((i=0 ; i <= 2 ; i++))
 convert ${FN}${i}.png -type truecolor ${FN}${i}.bmp
# display -resize 300% ${FN}${i}.bmp

echo Done!

Now, to get those bitmaps from the SD card into the proper place on the LCD panel…



1 Comment

Kenmore 158: Useful Unicode Glyphs

It turns out, for some reasons that aren’t relevant here, that I’ll be using the Adafruit Arduino LCD panel for the sewing machine control panel, at least to get started. In mulling that over, the notion of putting text on the buttons suggests using getting simple pictures with Unicode characters.

Herewith, some that may prove useful:

  • Needle stop up: ↥ = U+21A5
  • Needle stop up: ⤒=U+2912
  • Needle stop down: ⤓ = U+2913
  • Needle stop any: ↕ = U+2195
  • Needle stop any: ⟳ = U+27F3
  • Needle stop any: ⇅ = U+21C5
  • Rapid speed: ⛷ = U+26F7 (skier)
  • Rapid speed: 🐇  = U+1F407 (rabbit)
  • Slow speed: 🐢 = U+1F422 (turtle)
  • Dead slow: 🐌 = U+1F40C (snail)
  • Maximum speed: 🏃 = U+1F3C3 (runner)
  • Bobbin: ⛀ = U+26C0 (white draughts man)
  • Bobbin: ⛂ = U+26C2 (black draughts man)
  • Bobbin winding: 🍥 = U+1F365 (fish cake with swirl)

Of course, displaying those characters require a font with deep Unicode support, which may explain why your browser renders them as gibberish / open blocks / whatever. The speed glyphs look great on the Unicode table, but none of the fonts around here support them; I’m using the Droid font family to no avail.

Blocks of interest:

The links in the table of Unicode blocks lead to font coverage reports, but I don’t know how fonts get into those reports. The report for the Miscellaneous Symbols block suggested the Symbola font would work and a test with LibreOffice show it does:

Symbola font test

Symbola font test

An all-in-one-page Unicode symbol display can lock up your browser hard while rendering a new page.

Unicode is weird

1 Comment

Kenmore 158: Pulse Drive First Light

This worked right out of the box:

Pulse Drive - Tek 1 A-div

Pulse Drive – Tek 1 A-div

That’s roughly two half-cycles of the full-wave rectified AC with about 100 ms between pulses.

The upper trace comes from the differential amp, the lower trace from the Tek current probe at 1 A/div. The overall amp transconductance looks to be 1.3 A/V = 1.3 A/div, minus that small DC offset, so the ADC range is actually 6.5 A. That might be a bit too much, all things considered, but not worth changing right now.

Notice that the upper trace drops like a rock at the end of the pulse, while the Tek probe shows a gradual decrease. The missing current goes ’round and ’round through the flyback diode across the motor:

Pulse Drive - Flyback Diode - Tek 1 A-div

Pulse Drive – Flyback Diode – Tek 1 A-div

The Tek probe in the lower trace goes on the green wire connecting the diode to the bridge rectifier, oriented to match the diode polarity (+ current flows from motor to blue wire on collector to brown wire on rectifier to motor):

Motor flyback diode - installed

Motor flyback diode – installed

That nasty little spike in the middle of the diff amp output occurs when the collector voltage drops to zero and the ET227 shuts off, but the motor current continues to flow due to the winding inductance. In the first scope shot, the Tek probe doesn’t show any spikes in the motor current, because there aren’t any.

Compare that with the voltage and current of the motor running from an isolation transformer:

Rectified AC - 200 mA div - 875 RPM

Rectified AC – 200 mA div – 875 RPM

As the pulse repetition frequency increases, the motor speed goes up and the current goes down:

Pulse Drive - Fast - Tek 1 A-div

Pulse Drive – Fast – Tek 1 A-div

The dropouts between successive pairs of half-cycles show where the firmware shuts off the current and goes once around the main loop.

The Arduino code making that happen:

PedalPosition = ReadAI(PIN_PEDAL);
if (PedalPosition > 190) {
	BaseDAC.setVoltage(Cvt_mA_to_DAC(3000),false);					// give it a solid pulse
	MotorDrive.ADCvalue = SampleCurrent(PIN_CURRENT_SENSE);			// measure current = half cycle delay
	MotorDrive.ActualCurrent = Cvt_ADC_to_mA(MotorDrive.ADCvalue);
	printf("%5u, %5u, %5u, %5u, %5u, %5u, %5u\r\n",
	delay(3);														// finish rest of half cycle
	BaseDAC.setVoltage(0,false);									//  ... then turn it off

	delay(map(PedalPosition,190,870,100,0));						// pedal controls off time

The map() function flips the sense of the analog voltage coming from the pedal, so that more pedal pressure = higher voltage = lower delay. The pedal voltage produces ADC values from about 185 through 860, with a pleasant sigmoid shape that gives good speed control.

The maximum motor speed isn’t quite high enough for bobbin winding, but I like what I see so far!

Leave a comment

Kenmore 158: Recalibrated Hall Effect Sensor Amp

Reducing the differential amp gain fits a higher current into the Arduino’s fixed 5 V ADC range:

Hall Sensor Differential Amp

Hall Sensor Differential Amp

Those are 1% resistors, chosen from the heap for being pretty close to what I needed. Given that it’s an LM324 op amp, we’re not talking instrumentation grade results here.

The same calibration run that produced the DAC plot gave these values:

Current Calibrate - ADC - 270k Hall 2.7k opto

Current Calibrate – ADC – 270k Hall 2.7k opto

The linear fit gives the actual current, as seen by the Tek probe, for a given ADC reading.

The trimpot controls the offset voltage at zero current; working backwards, ADC = 0 corresponds to 140 mV, a bit higher than the actual 90 mV. Close enough, at least for a linear fit to eyeballed data, sez I.

Working forward, the maximum ADC value of 1023 corresponds to 4 A, which should suffice.

, ,

Leave a comment

Kenmore 158: Current & Shaft Speeds vs. Motor RPM

Now that the Arduino can set the current limiter, then measure the motor RPM, shaft RPM, and actual motor current, I can make plots like this:

Shaft speed and motor current vs RPM

Shaft speed and motor current vs RPM

The data comes from a routine that increments the setpoint current by 50 mA every five seconds, bouncing off 250 mA on the low end and 1 A on the high end, and writes the values to the serial port every half second. The actual current need not match the setpoint current, because it’s running open loop, and I haven’t done much in the way of calibration, so these represent interesting trends rather than dependable data points.

The eyeballometric slope down the middle of that blue smear comes out spot on 0.90, making the belt reduction 11.1 in good agreement with the results of those pulses.

The motor starts turning at 650 mA and will continue running down to maybe 500 mA, but with essentially zero low-end torque.

The horizontal range of green dots at each current setting shows that, as expected, the setpoint current has only a vague relation to the resulting motor speed: setting 800 mA will produce a speed between 5500 RPM and 9000 RPM, for sure. The actual motor current resulting from a given DAC output depends on the various transistor gains, all of which depend on temperature, which depends on how long the firmware has been running the motor at which speeds. Plenty of variation to go around.

The red points show that the actual motor current, as measured by the Hall effect sensor, generally lies below the green setpoint values, so better calibration is in order. Temperature effects turn accurate open-loop calibration into a fool’s errand, but we can do better than what you see there.

However, those red points do cluster much better, particularly between 6000 and 9000 RPM. You still can’t depend on the correlation, though, because the motor runs with a constant load here. In real life, the load will vary and so will the current required to maintain a given speed.

The green setpoints diverge from the red measurements at the high end, because the current limiter stops having much of an effect when the motor runs flat-out and sets its own current. After all, the original carbon-disk rheostat connected the line voltage directly across the motor, at which point the motor’s 100 W rating comes into play and limits the current to a nice sine wave with 1 A peaks.

All in all, it looks pretty good…


Leave a comment

Kenmore 158: Motor RPM Sensor Deglitching

The setscrew in the motor pulley lies directly in the path of the photosensor:

TCTR5000 Motor RPM Sensor - side view

TCTR5000 Motor RPM Sensor – side view

Which produces a glitch in the rising edge of the digital output as the pulley rotates from the dark to the light section:

Motor Sensor - Rising Edge Glitch

Motor Sensor – Rising Edge Glitch

The RPM signal goes to Arduino pin D2, where each falling edge triggers an interrupt handler:

const byte PIN_MOTOR_REV = 2;		// DI - IRQ 0 (must be D2)

... snippage...

void setup() {
... snippage ...

    attachInterrupt((PIN_MOTOR_REV - 2),ISR_Motor,FALLING);			// one IRQ / motor revolution

 ... snippage ...

The maximum motor speed is about 11 kRPM, so interrupts should be at least 5.5 ms apart and the digital input should be low. If that’s true, then the code updates a bunch of useful information:

struct pulse_t {
 byte Counter;
 unsigned long TimeThen;
 unsigned long Period;
 word RPM;
 byte State;

struct pulse_t Motor;

... snippage ...

// ISR to sample motor RPM sensor timing

void ISR_Motor(void) {

static unsigned long Now;


	Now = micros();

	if ((5000ul < (Now - Motor.TimeThen)) && !digitalRead(PIN_MOTOR_REV) ) {	// discard glitches
		Motor.Period = Now - Motor.TimeThen;
		Motor.TimeThen = Now;
		Motor.State = digitalRead(PIN_MOTOR_REV);		// always zero in a Physics 1 world


The scope trace shows that the handler takes about 7 µs to get control after the glitch (the left cursor should be on the falling edge, not the rising edge), so the input read occurs when the sensor output is over 4.5 V, causing the handler to discard this spurious interrupt.

Because Motor.Period is a four-byte unsigned long, the Arduino’s CPU must handle it in chunks. Rather than disable interrupts around each use, it’s better to read the value until two successive copies come back identical:

// Return current microsecond period without blocking ISR

unsigned long ReadTime(struct pulse_t *pTime) {

unsigned long Sample;

	do {
		Sample = pTime->Period;				// get all four bytes
	} while (Sample != pTime->Period);		//  repeat until not changed by ISR while reading

	pTime->Counter = 0;						// this is a slight race condition

	return Sample;

Because the interrupts don’t happen that often, the loop almost always executes only one time. On rare occasions, it’ll go back for another two values.

Converting the pulley rotation period into revolutions per minute goes like this:

		Motor.RPM = 60000000ul/ReadTime(&Motor);		// one (deglitched) pulse / rev

That’s easier than hiding the setscrew and it also discards any other glitches that may creep into D2

, ,

Leave a comment

Kenmore 158: Motor RPM and Shaft Sensor First Light

Using basically the same Arduino firmware as before, so the pedal scales the motor current without feedback:

Curr Sense RPM Spindle Pos

Curr Sense RPM Spindle Pos

The top trace is the motor current, sampled through the ferrite toroid / Hall effect sensor / differential amp, at about 525 mA/V, so the current limit along those flat tops is 630 mA. There’s a small initial spike leading into each flat top, where (I think) the rapidly rising collector voltage rams enough current through the Miller capacitance into the base to briefly push the collector current upward.

The next trace is the motor RPM sensor, ticking along at 14 revolutions in 160 ms = 87.5 rev/s = 5250 RPM. The glitch toward the right side comes from me hitting the scope’s STOP button to freeze the display in mid-trace. There’s no trace of the setscrew glitch, although that may be due to the compressed scale rather than the absence of the glitch.

The bottom trace is the shaft position sensor, with 1 rev in 125 ms = 8 rev/s = 480 RPM. It’s nicely divided into equal halves, which is what you’d expect from looking at the counterweight.

Under these conditions the speed ratio works out to 10.93, a whopping 9% over my original guesstimate.

Leave a comment