Advertisements

Kenmore 158 UI: Button Framework Data Structures

The trouble with grafting a fancy LCD on an 8 bit microcontroller like an Arduino is that there’s not enough internal storage for button images and barely enough I/O bandwidth to shuffle bitmap files from the SD card to the display. Nevertheless, that seems to be the least awful way of building a serviceable UI that doesn’t involve drilling holes for actual switches, indicators, and ugly 2×20 character LCD panels.

Rather than hardcoding the graphics into the Arduino program, though, it makes sense to build a slightly more general framework to handle button images and overall UI design, more-or-less independently of the actual program. I haven’t been able to find anything that does what I need, without doing a whole lot more, sooooo here’s a quick-and-dirty button framework.

Calling it a framework might be overstating the case: it’s just a data structure and some functions. It really should be a separate library, but …

After considerable discussion with the user community, the first UI can control just three functions:

  • Needle stop position: up, down, don’t care
  • Operating mode: follow pedal position, triggered single-step, normal run
  • Speed: slow, fast, ultra-fast

That boils down into a simple nine-button display, plus a tiny tenth button (in brown that looks red) at the top right:

Kenmore 158 UI buttons - first pass

Kenmore 158 UI buttons – first pass

The viciously skeuomorphic button shading should show the button status. For example, in the leftmost column:

  • The two upper button are “up”, with light glinting from upward-bulging domes
  • The lower button is “down”, with light glinting from its pushed-in dome

Frankly, that works poorly for me and the entire user community. The whole point of this framework is to let me re-skin the UI without re-hacking the underlying code; the buttons aren’t the limiting factor right now.

Anyhow.

The three buttons in each column are mutually exclusive radio buttons, but singleton checkbox buttons for firmware switches / modes would also be helpful, so there’s motivation to be a bit more general.

A struct defines each button:

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 NameStem[9];			// button BMP file name - stem only
};

The ID uniquely identifies each button in the rest of the code. That should be an enum, but for now I’m using an unsigned integer.

The Group integer will be zero for singleton buttons and a unique nonzero value for each radio button group. Only one button in each Group can be pressed at a time.

The Status indicates whether the button can be pushed and, if so, whether it’s up or down. Right now, the framework doesn’t handle disabled buttons at all.

The next four entries define the button’s position and size.

The pAction entry contains a pointer to the function that handles the button’s operation. It gets invoked whenever the touch screen registers a hit over the button, with an ID parameter identifying the button so you can use a single function for the entire group. I think it’ll eventually get another parameter indicating the desired Status, but it’s still early.

The NameStem string holds the first part of the file name on the SD card. The framework prefixes the stem with a default directory (“/UserIntf/”), suffixes it with the Status value (an ASCII digit ‘0’ through ‘9’), tacks on the extension (“.bmp”), and comes up with the complete file name.

An array of those structs defines the entire display:

struct button_t Buttons[] = {
	{ 1,	0, BT_UP,	  0,0,		 80,80,	DefaultAction,	"NdUp"},
	{ 2,	0, BT_UP,	  0,80,		 80,80,	DefaultAction,	"NdAny"},
	{ 3,	0, BT_DOWN,	  0,160,	 80,80,	DefaultAction,	"NdDn"},

	{ 4,	2, BT_DOWN,	 80,0,		120,80,	DefaultAction,	"PdRun"},
	{ 5,	2, BT_UP,	 80,80,		120,80,	DefaultAction,	"PdOne"},
	{ 6,	2, BT_UP,	 80,160,	120,80,	DefaultAction,	"PdFol"},

	{ 7,	3, BT_UP,	200,0,		 80,80,	DefaultAction,	"SpMax"},
	{ 8,	3, BT_DOWN,	200,80,		 80,80,	DefaultAction,	"SpMed"},
	{ 9,	3, BT_UP,	200,160,	 80,80,	DefaultAction,	"SpLow"},

	{10,	0, BT_UP,	311,0,		  8,8,	CountColor,		"Res"}
};

byte NumButtons = sizeof(Buttons) / sizeof(struct button_t);

Those values produce the screen shown in the picture. The first three buttons should be members of radio button group 1, but they’re singletons here to let me test that path.

Contrary to what you see, the button ID values need not be in ascending order, consecutive, or even continuous. The IDs identify a specific button, so as long as they’re a unique number in the range 1 through 255, that’s good enough. Yes, I faced down a brutal wrong-variable error and then fixed a picket-fence error.

The file name stems each refer to groups of BMP files on the SD card. For example, NdUp (“Needle stop up”) corresponds to the three files NdUp0.bmp, NdUp1.bmp, and NdUp2.bmp, with contents corresponding to the bstatus_t enumeration.

The constant elements of that array should come from a configuration file on the SD card: wrap a checksum around it, stuff it in EEPROM, and then verify it on subsequent runs. A corresponding array in RAM should contain only the Status values, with the array index extracted from the EEPROM data. That would yank a huge block of constants out of the all-too-crowded RAM address space and, even better, prevents problems from overwritten values; a trashed function pointer causes no end of debugging fun.

A more complex UI would have several such arrays, each describing a separate panel of buttons. There’s no provision for that right now.

Next: functions to make it march…

Advertisements

,

  1. #1 by madbodger on 2015-01-22 - 11:03

    I’m guessing you’ll eventually run out of patience with the Arduino’s limitations and go for a more powerful CPU. Were I you, I’d probably jump to a Teensy 3, which is a much more powerful ARM chip, with plug-ins that let you use the Arduino IDE with it, it’s small and less than twenty bucks.

    • #2 by Ed on 2015-01-22 - 11:28

      Nearly all the projects you see here eventually wind up in my Circuit Cellar columns, where I figure it’s better to take advantage of the Arduino network effect: they’re cheap, readily available, have good (if sometimes too enthusiastic) hardware support, and anybody can use them. That way, I don’t get into complex support requirements for obscure software stacks and unobtainable hardware.

      Fortunately, I never do anything too complex these days, although an asymmetric multiprocessor sewing machine control system definitely pushes the limits. [grin]

      • #3 by madbodger on 2015-01-22 - 12:09

        Yeah, I subscribe to Circuit Cellar (and Home Shop Machinist) and I generally read your articles first. It’s fascinating to have seen some of the work leading up to them, and then read the polished final version. And yes, it’s good to make projects as accessible as possible. I skip a lot of otherwise-interesting ones because they depend on things I don’t have access to, and that annoys me.

        • #4 by Ed on 2015-01-22 - 13:10

          the polished final version

          This blog really does serve as my shop notebook and build log, no doubt about it, and I’m glad to hear the final products look better!

          I’m rubbing really hard on the next column, though, having blown a solid week and far too much concentration trying to use an LCD GUI industrial controller as the sewing machine’s UI. That failed badly, leaving me to build the Arduino GUI manager and get the pulse drive control working and write the column by, uh, right about now.

          Kvetch, kvetch, kvetch.

          On the other paw, that debacle gutted the product launch and destroyed all credibility with their PR firm; I apparently give good new-product evaluation. No Baksheesh tag for them…

      • #5 by Red County Pete on 2015-01-22 - 15:47

        I had missed a recommendation for the Beaglebone Black on a post last summer, but found it on the trackback. (life got in the way…). The description on the Adafruit site seems encouraging, beyond the indefinite backorder. Does that still look like a good prospect for a multiaxis control? (It was for a telescope, though as you suggested, Linux CNC should suffice for software.)

        • #6 by Ed on 2015-01-22 - 20:26

          a good prospect for a multiaxis control?

          The BBB seems to be how LinuxCNC-based 3D printing gets done, albeit with a motor-driver cape that, last time I looked, seemed slightly less than unobtainable. I have’t been tracking the Machinekit branch of LinuxCNC, so I’m not sure how much of what I though I knew remains applicable; I think the HAL code has some differences and the USB layer was missing for a while.

          It would certainly take your mind off all your other problems… [grin]

          • #7 by Red County Pete on 2015-01-22 - 22:46

            slightly less than unobtainable

            Yeah, it’d fill my problem stack right well. [grin] (The BBB seems to be out for 3-4 months all by itself. Sigh.) Right now, Quality Shop Time is going to the Long Promised patio table from reclaimed cedar 2 x [246] stock. Have to assemble the frame, then fire up the thickness sander* to turn the 2 x 2 sticks into clean ones. Lost a lot of time to the Rhinovirus-on-steroids masquerading as a cold that wiped me out at Christmas.

            • Also long promised, and critical path for the cedar projects. Simple, once I figured out which design to steal adapt.

            Methinks I’ll get the other bits and pieces done for the ‘scope, then improvise for a while. Lots of ways to defur that particular feline.

  2. #8 by david on 2015-01-22 - 18:16

    Got to agree that the shading on those buttons is totally non-intuitive (maybe even disintuitive). But it gives me an excuse to post my favorite example of skeumorphism: https://en.wikipedia.org/wiki/Cattle_grid#Virtual_cattle_guards

    • #9 by Ed on 2015-01-22 - 20:33

      Heck, I wouldn’t walk on that grid without sticking my toe in a “slot” to be sure. On the other paw, I’m the type of guy who doesn’t step on steel plates in the sidewalk, Just Because.

      There’s a nice glass effect that I want to try on the buttons, but that’s so far off the critical path I can barely see it from here.

    • #10 by Red County Pete on 2015-01-22 - 22:16

      The county changed a couple of real grates to the virtual variety. Not enough cattle in those sections to determine just how well they work, but it’s a lot more comfortable driving over the latter.