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…
I’d probably just have a while(Serial1.available()) instead of the if() and do{}while.
Done!
Thanks…
fwiw I’ve done a lot of fairly long-term serial communications between arduinos using serial, successfully. Uptimes of weeks, surviving EMI, still running.
The cable gets strapped to the motor power cord for a few feet, so it’s in a harsh environment.
In any event, I’m not sure how I’d recognize a failure: there’s no visible feedback from the motor controller that the commands arrived properly.
That’s all in the nature of fine tuning, should the modified sewing machine turn out to be useful: acceptance testing & tweaks are in full effect!