#define ESP32

#include "HardwareSerial.h"
#include "Timers_ADI.h"

// Colors
#define WHITE_ADI 16777215
#define RED_ADI 16711680
#define BLACK_ADI 0
#define TEAL_ADI 65535
#define LT_RED_ADI 16002606    // #f42e2e
#define LT_YELLOW_ADI 16051202 // #f3ed2e

#define LIDARADDRESS 161

// Screens
// #define displayAddr 1
#define CONFIGSCREEN5 401
#define CONFIGSCREEN2 13
#define LOGOSCREEN 8
#define ARROWSCREEN 4
#define ARROWSCREENnb 1
#define NUMBERPAD 6       // 101
#define REPLYTIME 40      // 1 second max wait for reply //increase for wifi delays
#define SCREENTIMEOUT 300 // reset screen if no activity
#define LF 10
#define ESC 27
#define BACKDOOR 2430
#define ARROWSCREEN2 10
#define ARROWSCREEN2nb 11
#define CLARITYADSCREEN 20
#define CLARITYOKSCREEN 21
#define EVOWIFI0 22
#define EVOWIFI1 23
#define EVOWIFI2 24
#define EVOWIFI3 25

#define MAX_DISPLAYS 5
#define CLARITY_VER 14

int16_t rssiWIFI;

int displayAddr = 1;
int16_t countLF = 0;
byte instr[61];
int16_t indx;
int16_t address, xxx, yyy, zzz;
int16_t parmChoice = 0;
int16_t displayCase = 0;
int16_t lastDisplayCase = -1;
int16_t sendingDisplayCase = 0;
int32_t workvariable;
int16_t displayForward;

int8_t parmBank = 0; // multipage change
int motorFault = 0;
int obstructionFault = 0;
// int batteryFault = 0;
int battV;
int lidarObstruction = 0;
int lidarLearn;
int lidarTest = 0;
uint8_t slaveCommand;
int32_t clarityRepeatCount;
int32_t remoteParmFetchSerial = 0;
int32_t extParmsWrite[4];
int16_t remoteParmNum = -1;
uint16_t remoteParmLast = -1;
int16_t remoteParmCounter = -1;
int16_t parmRetryCounter = -1;

int attachedDisplays[MAX_DISPLAYS + 1];

/* define in main
  int cDOOROPEN = 0;
  int cDOORCLOSE = 0;
  int cDOORSTOP = 0;
  bool SCREENOPEN = 0;
  bool SCREENCLOSE = 0;
  bool SCREENSTOP = 0;
*/

int16_t X_OFFSET = 0;
int16_t Y_OFFSET = 0;

void init485(void)
{
  Serial2.begin(9600, SERIAL_8N1, 34, 27); // 485
  delay(400);

  setBacklightLevel(displayAddr, 50);
  delay(200);
}

void inSerial2(void) // run every pass in mainline
{
  while (Serial2.available() != 0) //      check char buffer
  {
    instr[indx] = Serial2.read(); //               store it
    if (instr[indx] == LF)
      countLF++;
    indx++;

    if (indx >= sizeof(instr))
    {
      indx = 0; //           oops overrun
      break;
    }
  }
}

/*
  Write Text
  <ESC>],aaa,xxx,yyy,TEXTTOPRINT…….,ccc<LF>
  x – column
  y – row
  TEXT - text characters to print 70 chars max
*/
void showDisplayText(int16_t adr, int16_t x, int16_t y, char *txtstr)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c],%d,%d,%d,%s,", 27, adr, x, y, txtstr);
  sendDisplayCommand(tmpstr);
}

/*
  Fill Screen
  <ESC>#,aaa,rgb,ccc<LF>
  rgb – Screen color
*/

void fillDisplay(int16_t adr, int32_t rgb)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c#,%d,%d,", 27, adr, rgb);
  sendDisplayCommand(tmpstr);
}

void setBacklightLevel(int16_t adr, int32_t bklt)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c*,%d,%d,", 27, adr, bklt);
  sendDisplayCommand(tmpstr);
}

/*
  Draw Rectangle
  <ESC>L,aaa,ttt,xxx,yyy,www,hhh,rgb,rrr,ccc<LF>
  ttt Type
  0 – default
  1 – filled
  xxx,yyy Rectangle origin
  www width
  hhh height
  rgb Outline or Fill Color
  rrr Corner radius 0 if not a rounded rectangle will be drawn

*/

void showDisplayRectangle(int16_t adr, int16_t mmm, int16_t x, int16_t y, int16_t www, int16_t hhh, int16_t ttt, uint32_t rgb, int16_t rrr)
{
  char tmpstr1[100];
  sprintf(tmpstr1, "%cR,%d,%d,%d,%d,%d,%d,%d,%d,%d,", 27, adr, mmm, x, y, www, hhh, ttt, rgb, rrr);
  Serial.println(tmpstr1);
  sendDisplayCommand(tmpstr1);
}

void showDisplayGraphic(int16_t adr, int16_t x, int16_t y, int16_t img, int16_t tp, int16_t tpenable)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c^,%d,%d,%d,%d,%d,%d,", 27, adr, x, y, img, tp, tpenable);
  sendDisplayCommand(tmpstr);
  lastDisplayedGraphic = img;
}

// Ping <ESC>%,aaa,ccc<LF>
void pingDisplay(int16_t adr)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c%c,%d,%d,", 27, 37, adr, displayCase);

  // sprintf(tmpstr, "%c%c,%d,", 27, 37, adr);
  sendDisplayCommand(tmpstr);
}

// Beep <ESC>%,aaa,ccc<LF>
void beepDisplay(int16_t adr)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c%c,%d,%d,", 27, 66, adr, 1000);
  sendDisplayCommand(tmpstr);
}

// Status <ESC>$,aaa,ccc<LF>
void statusDisplay(int16_t adr)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c$,%d,", 27, adr);
  sendDisplayCommand(tmpstr);
}

/*
  Text Attributes
  <ESC>2,font,bold,width,height,highlight,color,opacity<LF>
  font – 1 System_5x7 *bold 0 - off
  2 System_8x8 1 - on
  3(default) System_8x12
  4 System_12x6 *width 1 – 16 default 1
  5 MS_SanSerif8X12 *height 1 – 16 default 1
  6 dejaVuSans9pt
  7 dejaVuSansBold9pt highlight 0 – 65535 Text background color default BLACK
  8 dejaVuSansCondensed9pt
  9 System_3X6 color 0 – 65535 Text foreground color default WHITE
  10 plotted
  11 EGA 8 x 12 font *opacity 0 - transparent background pixels drawn
  1 – opaque background p
*/
void setTextAttributes(int16_t adr, int8_t fnt, int16_t bld, int16_t wdth, int16_t hgth, int32_t bkgd, int32_t frgd, int16_t opacity)
{
  char tmpstr[100];
  sprintf(tmpstr, "%c%c,%d,%d,%d,%d,%d,%d,%d,%d,", 27, 2, adr, fnt, bld, wdth, hgth, bkgd, frgd, opacity);
  sendDisplayCommand(tmpstr);
}

void showObstructionScreen(void)
{
  obstructionFault = 1;
  // displayCase = 710;
  cDOORCLOSE = 0; // v117
  DOORCLOSE = 0;
}

void setMotorFault(int f)
{
  motorFault = f + 1;
  // parm[33] = 0;
  parms.write(33, 0);
  faultNum = 2;
  faultAB = 0;
  faultData = 0;
}

void sendDisplayCommand(char *tmpstr)
{
  uint8_t i;
  uint16_t csum = 0;
  uint8_t bte;

  indx = 0;
  countLF = 0;
  Timers[COMMSREPLYTIMER] = 0; // restart it
  memset(instr, 0, sizeof(instr));

  // send the first part
  for (i = 0; tmpstr[i] != 0; i++)
    Serial2.write(tmpstr[i]);
  // if (VB) Serial.print(tmpstr);

  // calc the csum from ESC to final ","
  for (i = 0; tmpstr[i] != 0; i++)
    csum += tmpstr[i];
  if (VB)
  {
    // Serial.print("Raw CSUM = ");
    // Serial.println(csum);
  }
  csum = csum & 0xff;
  if (VB)
  {
    // Serial.print("Raw 8 bit CSUM = ");
    // Serial.println(csum);
  }
  csum = (0 - csum) & 0xff; // 2's comp
  bte = csum;
  if (VB)
  {
    // Serial.print("2's Comp CSUM = ");
    // Serial.println(bte);
  }
  if (bte == 255)
    bte = 0; // adjust it if 255
  if (VB)
  {
    // Serial.print("Check for 255 CSUM = ");
    // Serial.println(bte);
  }

  // send out the rest of it
  sprintf(tmpstr, "%u\n", bte);
  // if (VB) Serial.print(tmpstr);

  for (i = 0; tmpstr[i] != 0; i++)
    Serial2.write(tmpstr[i]);
}

void serviceDisplay(void)
{
  char tmpstr[80];
  float f;
  int16_t v10;
  int16_t v11;
  int16_t v;
  int i;
  // int16_t ig;

  switch (displayCase)
  {
  // init
  case 0:
    displayCase = 600; // multi displays always check
    displayAddr = 1;
    Timers[SCREENTIMEOUTTIMER] = 0;
    break;

  case 1:
    displayCase = 2;
    if (parm[12] == 0)
      displayCase = 140; // question screens
    displayAddr = 1;
    Timers[SCREENTIMEOUTTIMER] = 0;
    break;

  case 2:
    displayCase = 3;
    if (parm[54] != 0)
      displayCase = 300; // we have Lidar to configure
    displayAddr = 1;
    break;

  case 3:
    displayCase = 9;
    displayAddr = 1;
    break;

  // poll
  case 9:
    displayCase = 20; // logo screens.. scan displays

    break;

  case 10:
    displayCase = 22; // scan displays
    if (isDoorIdle() == 1)
      if (Timers[SCREENTIMEOUTTIMER] > SCREENTIMEOUT)
      {
        displayCase = 19; // too long
        Timers[SCREENTIMEOUTTIMER] = 0;
      }
    break;

  case 11: // check ioExpanders
    displayCase = 12;
    displayForward = 12;
    if (parm[25] != 0)
      displayCase = 200;
    break;

  case 12: // check lidar
    displayCase = 13;
    displayForward = 13;
    if (parm[54] != 0)
      displayCase = 310;
    break;

  case 13: // check if lidar learn
    displayCase = 14;
    if (lidarLearn)
      displayCase = 315;
    if (lidarTest)
      displayCase = 1310;
    break;

  case 14: // second slave motor?
    displayCase = 15;
    if (parm[52] != 0) // got slaves
    {
      displayCase = 350;
      displayForward = 15;
    }
    break;

  case 15:
    displayCase = 10;

    break;

  case 19: // shut off lidar
    displayCase = 20;
    displayForward = 20; // shut off lidar
    if (parm[54] != 0)
      displayCase = 310;
    break;

  //***LOGO Screen
  case 20:

    displayCase = 21;

    if (Timers[BLANKSCREENTIMER] > parm[67])
      fillDisplay(displayAddr, BLACK_ADI); // v525 clear screen
    else
    { // enough cycles?                   clarity enabled?
      if (parm[displayAddr + 130] >= CLARITY_VER)
      {
        if (parm[11] >= (parm[101] + clarityRepeatCount) &&
            (parm[101] > 0) &&
            (parm[9] == 0)) // let's show clarity screen
        {
          clarityRepeatCount = parm[11];
          showDisplayGraphic(displayAddr, 0, 0, CLARITYADSCREEN, 0, 0);
        }
        else
        {
          showDisplayGraphic(displayAddr, 0, 0, LOGOSCREEN, 0, 0);
        }
      }
      else
      {
        showDisplayGraphic(displayAddr, 0, 0, LOGOSCREEN, 0, 0);
      }
    }

    if (parm[9] != 0 && (Timers[BLANKSCREENTIMER] < parm[67]))
    {
      displayCase = 400;
      displayForward = 21;
    }

    // v525
    if (parm[67] != 0)
      if (Timers[BLANKSCREENTIMER] == -1)
        Timers[BLANKSCREENTIMER] = 0; // start it

    break;

  case 21:

    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      displayAddr++;
      displayCase = 22;

      // displayForward = displayCase; //v534 show WiFi RSSI
      // displayCase = 400; //v534

      if (attachedDisplays[displayAddr] == 0 || displayAddr > MAX_DISPLAYS) // no more displays
      {
        displayAddr = 1; // move on
      }
      else // got another display
      {
        displayCase = 20;
      }
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME * 3)
      displayCase = 999; // too long
    break;

  case 22:

    displayCase++;
    pingDisplay(displayAddr);

    break;

  case 23:

    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 22;
      displayForward = 999;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        Timers[BLANKSCREENTIMER] = -1; // v525 turn it off

        if (attachedDisplays[displayAddr] == 1)
        {
          X_OFFSET = 530;
          Y_OFFSET = 95;
        }
        else
        {
          X_OFFSET = 0;
          Y_OFFSET = 0;
        }

        if (check_TouchBox(200, 240, 280, 320) || check_TouchBox(700, 900, 400, 500)) // go to config
        {
          displayCase = 60; // numpad
          displayForward = 160;
          parmChoice = -1;

          if (parm[8] == 0)
            displayCase = 90; // config .. no access code
        }

        if (check_TouchBox(200, 240, 0, 50) || check_TouchBox(700, 900, 0, 100)) // go to wifi enabled
        {
          if (parm[9] == 0)
          {
            if (parm[displayAddr + 130] >= CLARITY_VER)
            {
              displayCase = 410;
              displayForward = 410;
            }
            else
            {
              displayCase = 420;
              displayForward = 420;
            }
            parms.write(9, 1);
          }
        }

        if (displayForward == 999) // none of above..wanna run door
        {
          clearMotorError();
          motorFault = 0;

          displayCase = 60; // go to keypad
          displayForward = 162;
          parmChoice = -1;
          if (parm[8] == 0)
            displayCase = 24;
        }
      }
      else // no touch
      {
        displayAddr++;
        displayCase = 22;
        if (attachedDisplays[displayAddr] == 00) // no more displays
        {
          displayAddr = 1; // move on

          displayForward = 22; // v125
          displayCase = 11;
        }
      }
    }

    if (obstructionFault)
    {
      displayCase = 710;
      break;
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 24:
    if (parm[1] == 0)                                               // normal v550
      if (parm[52] == 0)                                            // no slaves
        if (parm[3] != 0)                                           // bug-nobug
          showDisplayGraphic(displayAddr, 0, 0, ARROWSCREEN, 0, 0); // show the arrow with bug screen
        else
          showDisplayGraphic(displayAddr, 0, 0, ARROWSCREENnb, 0, 0); // show the arrow no bug screen

    if (parm[1] == 0)                                                // 2door v550 master
      if (parm[52] != 0)                                             // got slaves
        if (parm[3] != 0)                                            // bug-nobug
          showDisplayGraphic(displayAddr, 0, 0, ARROWSCREEN2, 0, 0); // show the arrow with bug screen
        else
          showDisplayGraphic(displayAddr, 0, 0, ARROWSCREEN2nb, 0, 0); // show the arrow no bug screen

    displayCase++;
    lidarObstruction = 0;
    break;

  case 25:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;

    if (Timers[COMMSREPLYTIMER] > REPLYTIME * 3)
      displayCase = 999; // too long
    break;

  case 26:
    displayForward = 27;
    if (getMotorError())
    {
      // parm[33] = 0;
      parms.write(33, 0);
      displayCase = 810;
      faultNum = 4;
      faultAB = 0;
      faultData = getMotorError();
      if (parm[52] != 0)
        displayCase = 360;
      qualifyFault(33, getMotorError());
      break;
    }
    else
      restoreFault(33);
    if (motorFault)
    {
      parms.write(33, 0);
      displayCase = 810;
      faultNum = 2;
      faultAB = 0;
      faultData = motorFault;
      if (parm[52] != 0)
        displayCase = 360;
      qualifyFault(34, motorFault);
      break;
    }
    else
      restoreFault(34);

    if (obstructionFault)
    {
      parms.write(33, 0);
      displayCase = 710;
      faultNum = 1;
      faultAB = 0;
      faultData = 0;
      qualifyFault(25, 0);
      if (parm[52] != 0)
        displayCase = 360;
      break;
    }
    else
      restoreFault(25);

    if (LowBattery() == 1)
    {
      parms.write(33, 0);
      displayCase = 910;
      if (parm[52] != 0)
        displayCase = 360;
      cDOOROPEN = 0;
      cDOORCLOSE = 0;
      cDOORSTOP = 1;
      qualifyFault(2, 0); // push
      break;
    }
    else
      restoreFault(2); // push

    if (isDoorIdle() == 1)
      if (Timers[SCREENTIMEOUTTIMER] > SCREENTIMEOUT)
      {
        displayCase = 19; // too long
        Timers[SCREENTIMEOUTTIMER] = 0;
        break;
      }

    displayCase++;
    pingDisplay(displayAddr);
    break;

  case 27:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      if ((xxx + yyy) >= 0) // got a touch
      {
        Serial.print(attachedDisplays[displayAddr]);
        if (attachedDisplays[displayAddr] == 2) // 2.4"
        {
          displayCase = 28;
          X_OFFSET = 0;
          Y_OFFSET = 0;
          Serial.println(" 2.4 inch");
        }
        if (attachedDisplays[displayAddr] == 1) // 5"
        {
          displayCase = 528;
          X_OFFSET = 530;
          Y_OFFSET = 95;
          Serial.println(" 5 inch");
        }
      }
      else
      {
        displayCase = 30; // no touch.. ping again
        // displayCase = 200;
        // displaycase = 10; //test
        displayForward = 26;
        cDOOROPEN = 0;
        cDOORCLOSE = 0;
        cDOORSTOP = 0;
        SCREENCLOSE = SCREENOPEN = SCREENSTOP = 0;
      }
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 28: // process touch 2.4"
    displayForward = 26;
    displayCase = 30;
    if (parm[54] != 0)
      displayCase = 310;

    if (parm[3] != 0) // we gotta bug screen
    {
      if (check_TouchBox(0, 118, 0, 132))
      {
        // Serial.println("DOWN Arrow");

        cDOORCLOSE = 1;
      }

      if (check_TouchBox(119, 230, 0, 132))
      {
        // Serial.println("UP Arrow");

        cDOOROPEN = 1;
      }

      if (check_TouchBox(67, 167, 143, 271))
      {
        // Serial.println("DOOR STOP");
        cDOORSTOP = 1;
      }
    }
    else
    {
      if (check_TouchBox(0, 118, 0, 193))
      {
        // Serial.println("DOWN Arrow");

        cDOORCLOSE = 1;
      }
      if (check_TouchBox(119, 230, 0, 193))
      {
        // Serial.println("UP Arrow");

        cDOOROPEN = 1;
      }
      if (check_TouchBox(54, 174, 194, 320))
      {
        // Serial.println("DOOR STOP");
        cDOORSTOP = 1;
      }
    }

    if (check_TouchBox(12, 80, 273, 303))
    {
      displayText("Screen DOWN");
      if (parm[28] == 0)
        rfSendKey(RF_KEY_DN, myCHAN);
      else
        SCREENCLOSE = 1; // v521
    }

    if (check_TouchBox(80, 153, 273, 303))
    {
      displayText("Screen STOP");

      if (parm[28] == 0)
        rfSendKey(RF_KEY_STOP, myCHAN);
      else
        SCREENSTOP = 1; // v521
    }

    if (check_TouchBox(153, 227, 273, 303))
    {
      displayText("Screen UP");
      if (parm[28] == 0)
        rfSendKey(RF_KEY_UP, myCHAN);
      else
        SCREENOPEN = 1; // v521
    }

    xxx = yyy = 0;
    break;

  case 528: // process touch 5"
    displayForward = 26;
    displayCase = 30;
    if (parm[54] != 0)
      displayCase = 310;

    //*** single door
    if (parm[52] == 0) // single door normal
    {
      if (parm[3] != 0) // we gotta bug screen
      {
        if (check_TouchBox(249, 395, 268, 335)) // 5"
        {
          Serial.println("5 DOWN Arrow");
          cDOORCLOSE = 1;
        }
        if (check_TouchBox(249, 395, 185, 239)) // 5"
        {
          Serial.println("5 UP Arrow");
          cDOOROPEN = 1;
        }
        if (check_TouchBox(249, 395, 358, 426)) // 5"
        {
          Serial.println("5 DOOR STOP");
          cDOORSTOP = 1;
        }

        // bug screen stuff
        if (check_TouchBox(412, 545, 286, 338)) // 5"
        {
          Serial.println("Screen DOWN");
          if (parm[28] == 0)
            rfSendKey(RF_KEY_DN, myCHAN);
          else
            SCREENCLOSE = 1; // v521
        }
        if (check_TouchBox(412, 545, 363, 418)) // 5"
        {
          Serial.println("Screen STOP");
          if (parm[28] == 0)
            rfSendKey(RF_KEY_STOP, myCHAN);
          else
            SCREENSTOP = 1; // v521
        }
        if (check_TouchBox(412, 545, 200, 264)) // 5"
        {
          Serial.println("Screen UP");
          if (parm[28] == 0)
            rfSendKey(RF_KEY_UP, myCHAN);
          else
            SCREENOPEN = 1; // v521
        }
      }
      else // no bug screen
      {
        if (check_TouchBox(335, 485, 268, 335)) // 5"
        {
          Serial.println("DOWN Arrow");
          cDOORCLOSE = 1;
        }
        if (check_TouchBox(335, 485, 185, 239)) // 5"
        {
          Serial.println("UP Arrow");
          cDOOROPEN = 1;
        }
        if (check_TouchBox(335, 485, 358, 426)) // 5"
        {
          Serial.println("DOOR STOP");
          cDOORSTOP = 1;
        }
      }
    } // end of single door

    //*** double door
    if (parm[52] == 1) // 2 door master
    {
      if (parm[3] != 0) // we gotta bug screen
      {
        if (check_TouchBox(35, 35 + 163, 259, 259 + 77)) // 5"
        {
          Serial.println("Master 5 DOWN Arrow");
          cDOORCLOSE = 1;
        }
        if (check_TouchBox(35, 35 + 163, 172, 172 + 77)) // 5"
        {
          Serial.println("Master 5 UP Arrow");
          cDOOROPEN = 1;
        }
        if (check_TouchBox(35, 35 + 163, 357, 357 + 77)) // 5"
        {
          Serial.println("Master 5 DOOR STOP");
          cDOORSTOP = 1;
        }

        // slave door
        if (check_TouchBox(426, 582, 259, 259 + 77)) // 5"
        {
          Serial.println("Slave 5 DOWN Arrow");
          slaveCommand = 4;
        }
        if (check_TouchBox(426, 582, 172, 172 + 77)) // 5"
        {
          Serial.println("Slave 5 UP Arrow");
          slaveCommand = 5;
        }
        if (check_TouchBox(426, 582, 357, 357 + 77)) // 5"
        {
          Serial.println("Slave 5 DOOR STOP");
          slaveCommand = 6;
        }

        // master's bug screen

        if (check_TouchBox(218, 218 + 135, 274, 274 + 65)) // 5"
        {
          Serial.println("Master Screen DOWN");
          if (parm[28] == 0)
            rfSendKey(RF_KEY_DN, myCHAN);
          else
            SCREENCLOSE = 1; // v521
        }
        if (check_TouchBox(218, 218 + 135, 351, 351 + 65)) // 5"
        {
          Serial.println("Master Screen STOP");
          if (parm[28] == 0)
            rfSendKey(RF_KEY_STOP, myCHAN);
          else
            SCREENSTOP = 1; // v521
        }
        if (check_TouchBox(218, 218 + 135, 194, 194 + 65)) // 5"
        {
          Serial.println("Master Screen UP");
          if (parm[28] == 0)
            rfSendKey(RF_KEY_UP, myCHAN);
          else
            SCREENOPEN = 1; // v521
        }

        // slave's bug screen
        if (check_TouchBox(605, 605 + 135, 274, 274 + 65)) // 5"
        {
          Serial.println("Slave Screen DOWN");
          slaveCommand = 1;
          // rfSendKey(RF_KEY_DN, myCHAN + 1);
          // SCREENCLOSE = 1;
        }
        if (check_TouchBox(605, 605 + 135, 351, 351 + 65)) // 5"
        {
          Serial.println("Screen STOP");
          // rfSendKey(RF_KEY_STOP, myCHAN + 1);
          slaveCommand = 2;
          // SCREENSTOP = 1;
        }
        if (check_TouchBox(605, 605 + 135, 194, 194 + 65)) // 5"
        {
          Serial.println("Screen UP");
          // rfSendKey(RF_KEY_UP, myCHAN + 1);
          slaveCommand = 3;
          // SCREENOPEN = 1;
        }
      }
      else // no bug screen
      {
        if (check_TouchBox(128, 284, 268, 335)) // 5"
        {
          Serial.println("Master DOWN Arrow");
          cDOORCLOSE = 1;
        }
        if (check_TouchBox(128, 284, 173, 245)) // 5"
        {
          Serial.println("Master UP Arrow");
          cDOOROPEN = 1;
        }
        if (check_TouchBox(128, 284, 358, 426)) // 5"
        {
          Serial.println("Master DOOR STOP");
          cDOORSTOP = 1;
        }

        if (check_TouchBox(515, 671, 259, 259 + 77)) // 5"
        {
          Serial.println("Slave DOWN Arrow");
          slaveCommand = 4;
        }
        if (check_TouchBox(515, 671, 172, 172 + 77)) // 5"
        {
          Serial.println("Slave UP Arrow");
          slaveCommand = 5;
        }
        if (check_TouchBox(515, 671, 357, 357 + 77)) // 5"
        {
          Serial.println("Slave DOOR STOP");
          slaveCommand = 6;
        }
      }
    }

    xxx = yyy = 0;
    break;

  case 30:
    displayCase = 31;
    if (parm[25] != 0)
      displayCase = 200; // ioExpanders?
    displayForward = 31;
    break;

  case 31:
    displayCase = 32;
    if (parm[52] != 0)
      displayCase = 350; // slave door?
    displayForward = 32;
    break;

  case 32:
    displayCase = 26;
    if (parm[54] != 0)
      displayCase = 310; // Lidar?
    displayForward = 26;
    break;

    /*
        case 29:
          Serial.print(workvariable);
          Serial.print("  Parm 8 = ");
          Serial.println(parm[8]);
          if ((workvariable == parm[8]) || (workvariable == BACKDOOR))
            displayCase = 24;
          else
            displayCase = 0;
          break;



        case 80:
          Serial.println(workvariable);
          Serial.print("  Parm 8 = ");
          Serial.println(parm[8]);
          if ((workvariable == parm[8]) || (workvariable == BACKDOOR))
            displayCase = 39;//multipage change
          else
            displayCase = 0;
          break;
    */

    ////***** Parms Screens
#define PARMSPERPAGE 21 // 33  //must be divisable by 3

  case 39: // multipage change
    displayCase++;
    parmBank = 0;
    break;

  case 40:
    Timers[SCREENTIMEOUTTIMER] = 0;
    fillDisplay(displayAddr, BLACK_ADI);
    // delay(200);
    displayCase++;
    break;

  case 41:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 42:
    if (attachedDisplays[displayAddr] == 1) // 5 inch
      setTextAttributes(displayAddr, 7, 0, 0, 0, 0, TEAL_ADI, 0);
    else
      setTextAttributes(displayAddr, 3, 0, 0, 0, 0, TEAL_ADI, 0);

    displayCase++;
    break;

  case 43:
    if (countLF >= 2) // got our response
    {
      displayCase++;
    }
    parmChoice = 1;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 1; // too long
    break;

  case 44: // multipage change

    if (attachedDisplays[displayAddr] == 1) // 5 inch
      sprintf(tmpstr, "%2d# %7d      %3d# %7d      %3d#%7d",
              parmChoice + (parmBank * PARMSPERPAGE),
              parm[parmChoice + (parmBank * PARMSPERPAGE)],
              parmChoice + (PARMSPERPAGE / 3) + (parmBank * PARMSPERPAGE),
              parm[parmChoice + (PARMSPERPAGE / 3) + (parmBank * PARMSPERPAGE)],
              parmChoice + ((PARMSPERPAGE / 3) * 2) + (parmBank * PARMSPERPAGE),
              parm[parmChoice + ((PARMSPERPAGE / 3) * 2) + (parmBank * PARMSPERPAGE)]);
    else
      sprintf(tmpstr, "%2d#%5d %2d#%5d %2d#%5d",
              parmChoice + (parmBank * 33),
              parm[parmChoice + (parmBank * 33)],
              parmChoice + 11 + (parmBank * 33),
              parm[parmChoice + 11 + (parmBank * 33)],
              parmChoice + 22 + (parmBank * 33),
              parm[parmChoice + 22 + (parmBank * 33)]);

#define PARMLINEHEIGHT (380 / (PARMSPERPAGE / 3))

    if (attachedDisplays[displayAddr] == 1) // 5 inch
      // showDisplayText(displayAddr, 5, ((parmChoice - 1) * 40) + 4, tmpstr);
      showDisplayText(displayAddr, 5, ((parmChoice - 0) * PARMLINEHEIGHT), tmpstr);
    else
      showDisplayText(displayAddr, 5, ((parmChoice - 1) * 27) + 4, tmpstr);

    displayCase++;
    break;

  case 45:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      displayCase = 44;
      if (attachedDisplays[displayAddr] == 1) // 5 inch
      {
        if (++parmChoice > (PARMSPERPAGE / 3))
          displayCase = 47;
      }
      else // 2"
      {
        if (++parmChoice > (11))
          displayCase = 47;
      }
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 47: // multipage change

    if (attachedDisplays[displayAddr] == 1) // 5 inch
    {
      switch (parmBank)
      {
      case 0:
        showDisplayText(displayAddr, 150, 442, "             EXIT        NEXT");
        break;

      case (PARMSIZE / PARMSPERPAGE):
        showDisplayText(displayAddr, 150, 442, " PREV        EXIT            ");
        break;

      default:
        showDisplayText(displayAddr, 150, 442, " PREV        EXIT        NEXT");
        break;
      }
    }
    else
    {
      if (parmBank == 0)
        showDisplayText(displayAddr, 110, 300 - 2, "EXIT    NEXT");
      else
        showDisplayText(displayAddr, 110, 300 - 2, "EXIT    PREV");
    }

    displayCase++;
    break;

  case 48:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 49:
    displayCase = 50;
    if (attachedDisplays[displayAddr] == 1) // 5 inch
      displayCase = 550;

    pingDisplay(displayAddr);
    break;

  case 50:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 49;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        if (yyy < 298) // v115
        {
          parmChoice = (yyy / 27); // v117

          parmChoice++;

          if (xxx > 87)
            parmChoice += 11;
          if (xxx > 162)
            parmChoice += 11;

          parmChoice = parmChoice + (parmBank * 33); // multipage

          Serial.print("Parm: ");
          Serial.println(parmChoice);
          displayCase = 60; // go to numberpad
          displayForward = 164;
        }
        else // Exit
        {
          if (xxx < 160)
          {
            displayCase = 0; // EXIT
            Serial.println("Exit to LOGO");
          }
          if (xxx >= 160) // NEXT PREV
          {
            displayCase = 40;
            if (parmBank == 0)
              parmBank = 1;
            else
              parmBank = 0; // toggle it //multipage
          }
        }
      }
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 550: // 5"
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 49;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        if (yyy < 420) // parm area
        {
          if (yyy > 20)
          {
            parmChoice = ((yyy - 20) / PARMLINEHEIGHT);

            parmChoice++;

            if (xxx > 268)
              parmChoice += (PARMSPERPAGE / 3);
            if (xxx > (268 * 2))
              parmChoice += (PARMSPERPAGE / 3);

            parmChoice = parmChoice + (parmBank * PARMSPERPAGE); // multipage

            Serial.print("Parm: ");
            Serial.println(parmChoice);
            if (parmChoice < PARMSIZE)
            {
              displayCase = 60; // go to numberpad
              displayForward = 164;
            }
          }
        }

        else //  prev/exit/next area
        {
          if (xxx > 270)
            if (xxx < (800 - 270))
            {
              displayCase = 0; // EXIT
              Serial.println("Exit to LOGO");
            }

          if (parmBank < ((PARMSIZE / PARMSPERPAGE)))
            if (xxx >= (800 - 270)) // NEXT
            {
              displayCase = 40;
              parmBank++; // toggle it //multipage
            }

          if (xxx < (270)) // PREV
          {
            displayCase = 40;
            if (parmBank > 0)
              parmBank--;
          }
        }
      }
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  //****NUMBERPAD Screens
  case 60:
    Timers[SCREENTIMEOUTTIMER] = 0;

    showDisplayGraphic(displayAddr, 0, 0, NUMBERPAD, 0, 0);

    if (attachedDisplays[displayAddr] == 2) // V996
    {                                       // 2.4" screen
      // showDisplayGraphic(displayAddr, 0, 0, NUMBERPAD, 0, 0);
      X_OFFSET = 0;
      Y_OFFSET = 0;
    }
    else
    {
      // showDisplayGraphic(displayAddr, 0, 0, NUMBERPAD5, 0, 0);
      X_OFFSET = 530;
      Y_OFFSET = 95;
    }

    displayCase++;
    break;

  case 61:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    // Serial.println(instr);
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME * 3))
      displayCase = 999; // too long
    break;

  case 62:
    setTextAttributes(displayAddr, 5, 0, 0, 0, BLACK_ADI, WHITE_ADI, 0);
    displayCase++;
    break;

  case 63:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 64:
    if (parmChoice > 0) // parm entry
      sprintf(tmpstr, "#%d", parmChoice);
    else // number entry
      sprintf(tmpstr, ">");

    if (attachedDisplays[displayAddr] == 1) // 5"
      showDisplayText(displayAddr, 462, 28, tmpstr);
    else
      showDisplayText(displayAddr, 15, 13, tmpstr);

    displayCase++;
    break;

  case 65:
    sendingDisplayCase = displayCase;
    workvariable = 0;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 66:
    displayCase++;
    pingDisplay(displayAddr);
    break;

  case 67:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 66;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        displayCase = 68;
        if (attachedDisplays[displayAddr] == 1) // 5"
        {
          if (check_TouchBox(440, 557, 64, 146))
            workvariable = (workvariable * 10) + 7;
          if (check_TouchBox(557, 672, 64, 146))
            workvariable = (workvariable * 10) + 8;
          if (check_TouchBox(672, 786, 64, 146))
            workvariable = (workvariable * 10) + 9;
          if (check_TouchBox(440, 557, 146, 229))
            workvariable = (workvariable * 10) + 4;
          if (check_TouchBox(557, 672, 146, 229))
            workvariable = (workvariable * 10) + 5;
          if (check_TouchBox(672, 786, 146, 229))
            workvariable = (workvariable * 10) + 6;
          if (check_TouchBox(440, 557, 229, 308))
            workvariable = (workvariable * 10) + 1;
          if (check_TouchBox(557, 672, 229, 308))
            workvariable = (workvariable * 10) + 2;
          if (check_TouchBox(672, 786, 229, 308))
            workvariable = (workvariable * 10) + 3;
          if (check_TouchBox(557, 672, 308, 391))
            workvariable = (workvariable * 10) + 0;
          if (check_TouchBox(672, 786, 308, 391))
            workvariable = (workvariable * -1);
          if (check_TouchBox(440, 557, 308, 391))
            workvariable = (workvariable /= 10);

          if (check_TouchBox(440, 672, 391, 469)) // ENTER
          {
            displayCase = displayForward;
          }

          if (check_TouchBox(672, 786, 391, 469)) // Exit
          {
            displayCase = displayForward + 1;
          }
        }
        else // 2"
        {
          if (check_TouchBox(0 + X_OFFSET, 72 + X_OFFSET, 40 + Y_OFFSET, 98 + Y_OFFSET))
            workvariable = (workvariable * 10) + 7;
          if (check_TouchBox(73 + X_OFFSET, 154 + X_OFFSET, 40 + Y_OFFSET, 98 + Y_OFFSET))
            workvariable = (workvariable * 10) + 8;
          if (check_TouchBox(155 + X_OFFSET, 240 + X_OFFSET, 40 + Y_OFFSET, 98 + Y_OFFSET))
            workvariable = (workvariable * 10) + 9;
          if (check_TouchBox(0 + X_OFFSET, 72 + X_OFFSET, 98 + Y_OFFSET, 154 + Y_OFFSET))
            workvariable = (workvariable * 10) + 4;
          if (check_TouchBox(73 + X_OFFSET, 154 + X_OFFSET, 98 + Y_OFFSET, 154 + Y_OFFSET))
            workvariable = (workvariable * 10) + 5;
          if (check_TouchBox(155 + X_OFFSET, 240 + X_OFFSET, 98 + Y_OFFSET, 154 + Y_OFFSET))
            workvariable = (workvariable * 10) + 6;
          if (check_TouchBox(0 + X_OFFSET, 72 + X_OFFSET, 154 + Y_OFFSET, 210 + Y_OFFSET))
            workvariable = (workvariable * 10) + 1;
          if (check_TouchBox(73 + X_OFFSET, 154 + X_OFFSET, 154 + Y_OFFSET, 210 + Y_OFFSET))
            workvariable = (workvariable * 10) + 2;
          if (check_TouchBox(154 + X_OFFSET, 240 + X_OFFSET, 154 + Y_OFFSET, 210 + Y_OFFSET))
            workvariable = (workvariable * 10) + 3;
          if (check_TouchBox(73 + X_OFFSET, 154 + X_OFFSET, 211 + Y_OFFSET, 264 + Y_OFFSET))
            workvariable = (workvariable * 10) + 0;

          if (check_TouchBox(154 + X_OFFSET, 240 + X_OFFSET, 211 + Y_OFFSET, 264 + Y_OFFSET))
            workvariable = (workvariable * -1);
          if (check_TouchBox(0 + X_OFFSET, 72 + X_OFFSET, 211 + Y_OFFSET, 264 + Y_OFFSET))
            workvariable = (workvariable /= 10);

          if (check_TouchBox(1 + X_OFFSET, 153 + X_OFFSET, 264 + Y_OFFSET, 319 + Y_OFFSET)) // ENTER
          {
            displayCase = displayForward;
          }

          if (check_TouchBox(154 + X_OFFSET, 240 + X_OFFSET, 264 + Y_OFFSET, 319 + Y_OFFSET)) // Exit
          {
            displayCase = displayForward + 1;
          }
        }
      }
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    if (Timers[SCREENTIMEOUTTIMER] > SCREENTIMEOUT)
      displayCase = 999; // v525  too long, no activity

    break;

  case 266: // go here to wait for touch release
    displayCase++;
    pingDisplay(displayAddr);
    break;

  case 267:
    // sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      if ((xxx + yyy) >= 0) // or got a valid touch
        displayCase = 266;
      else
        displayCase = 66;
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    if (Timers[SCREENTIMEOUTTIMER] > SCREENTIMEOUT)
      displayCase = 999; // v525  too long, no activity

    break;

  case 68:
    Timers[SCREENTIMEOUTTIMER] = 0;

    if (attachedDisplays[displayAddr] == 1) // 5"
      showDisplayRectangle(displayAddr, 1, 510, 20, 140, 20, 1, BLACK_ADI, 1);
    else // 2"
      showDisplayRectangle(displayAddr, 1, 88, 13, 140, 20, 1, BLACK_ADI, 1);

    displayCase++;
    break;

  case 69:
    sendingDisplayCase = displayCase;

    if (countLF >= 2) // got our response
      displayCase++;

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 70:
    sprintf(tmpstr, "%10d", workvariable);

    if (attachedDisplays[displayAddr] == 1) // 5"
      showDisplayText(displayAddr, 584, 28, tmpstr);
    else // 2"
      showDisplayText(displayAddr, 95, 13, tmpstr);
    displayCase = 267;
    break;

  //***** Numpad Landing Screens

  // access entry
  case 160: // enter
    displayCase = 0;
    if ((parm[8] == workvariable) || (workvariable == BACKDOOR))
      displayCase = 90;
    break;
  case 161: // exit
    displayCase = 0;
    break;

  // access run door
  case 162: // enter
    displayCase = 0;
    if ((parm[8] == workvariable) || (workvariable == BACKDOOR))
      displayCase = 24;
    break;
  case 163: // exit
    displayCase = 0;
    break;

  // parm change
  case 164: // enter
    displayCase = 40;

    if ((parmChoice == 30) || (parmChoice == 31) || (parmChoice == 32)) // virtual writes
    {
      parms.write(parmChoice, workvariable);
      if (parmChoice == 32)
        displayCase = 300; // send it
    }
    else // not virtual write
    {
      parms.write(parmChoice, workvariable);
    }
    break;

  case 165: // exit
    displayCase = 40;
    break;

  case 166: // enter
    displayCase = 159;
    parms.write(parmChoice, workvariable);
    break;

  case 167: // exit
    displayCase = 159;
    break;

    //***** Config Screen

  case 90:
    Timers[SCREENTIMEOUTTIMER] = 0;
    if (attachedDisplays[displayAddr] == 1) // 5 inch
      showDisplayGraphic(displayAddr, 0, 0, CONFIGSCREEN5, 0, 0);
    else
      showDisplayGraphic(displayAddr, 0, 0, CONFIGSCREEN2, 0, 0);
    displayCase++;
    delay(1000); // getting double presses
    break;

  case 91:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME * 3))
      displayCase = 999; // too long
    break;

  case 92:
    displayCase++;
    pingDisplay(displayAddr);

    break;

  case 93:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 92;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        // displayCase = 0;//v515

        if (attachedDisplays[displayAddr] == 1) // 5 inch
        {
          Serial.println("5 inch display");
          if (check_TouchBox(257, 225 + 293, 28, 28 + 59)) // parms button
          {
            displayCase = 39;
            Serial.println("Parms Button");

            parmChoice = -1;
          }
          if (check_TouchBox(257, 225 + 293, 105, 105 + 105)) // learn-level open button
          {
            displayCase = 92;

            parms.write(48, motorGetPosition(0) - motorGetPosition(1));
            parms.write(40, motorGetPosition(0));
            Serial.printf("Learn Level P48 = %d, P40 = %d", parm[48], parm[40]);
          }
          if (check_TouchBox(257, 225 + 293, 231, 231 + 59)) // learn-close button
          {
            displayCase = 92;
            Serial.println("Learn Close");
            parms.write(39, motorGetPosition(0));
          }
          if (check_TouchBox(257, 225 + 293, 312, 312 + 49)) // fenetex pgm button
          {
            displayCase = 92;
            displayText("Learn Fenetex");
            rfSendKey(RF_KEY_PROG, myCHAN);
            lidarLearn = 1;
          }

          // 515 add
          if (check_TouchBox(257, 225 + 293, 393, 393 + 49)) // exit
          {
            displayCase = 0;
            Serial.println("Exit");
          }
        }
        else // 2 inch
        {

          Serial.println("2 inch display");
          if (check_TouchBox(22, 22 + 195, 17, 17 + 39)) // parms button
          {
            displayCase = 39;
            Serial.println("Parms Button");

            // displayCase = 60;
            // displayForward = 80;
            parmChoice = -1;
          }

          if (check_TouchBox(22, 22 + 195, 68, 68 + 70)) // learn-level button
          {
            displayCase = 92;

            parms.write(48, motorGetPosition(0) - motorGetPosition(1));
            parms.write(40, motorGetPosition(0));
            Serial.printf("Learn Level P48 = %d, P40 = %d", parm[48], parm[40]);
          }
          if (check_TouchBox(22, 22 + 195, 152, 152 + 39)) // learn-close button
          {
            displayCase = 92;
            Serial.println("Learn Close");
            parms.write(39, motorGetPosition(0));
          }
          if (check_TouchBox(22, 22 + 195, 206, 206 + 39)) // fenetex pgm button
          {
            displayCase = 92;
            Serial.println("Learn Fenetex");
            rfSendKey(RF_KEY_PROG, myCHAN);
            lidarLearn = 1;
          }
          // 515 add
          if (check_TouchBox(22, 22 + 195, 263, 263 + 39)) // exit
          {
            displayCase = 0;
            Serial.println("Exit");
          }
        }
      }
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

    ////***** Setup Screens

  case 140:
    Timers[SCREENTIMEOUTTIMER] = 0;
    fillDisplay(displayAddr, BLACK_ADI);
    // delay(200);
    displayCase++;
    break;

  case 141:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 142:
    setTextAttributes(1, 5, 0, 0, 0, 0, TEAL_ADI, 0);
    displayCase++;
    break;

  case 143:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

    //***first question

  case 144:
    sprintf(tmpstr, "Bug Screen? Y / N");
    // sprintf(tmpstr, "Shade/Scrn? Y / N");
    showDisplayText(1, 5, 30, tmpstr);
    displayCase++;
    break;

  case 145:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 146:
    displayCase++;
    pingDisplay(1);

    break;

  case 147:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 146;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        if (check_TouchBox(156, 189, 25, 60)) // yes
        {
          displayCase = 148;
          parms.write(3, 5);
        }
        if (check_TouchBox(190, 240, 25, 60)) // NO
        {
          displayCase = 148;
          parms.write(3, 0);
        }
      }
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  //***** second question
  case 148:
    // sprintf(tmpstr, "Bug Screen? Y / N");
    sprintf(tmpstr, "Gasket?     Y / N");
    showDisplayText(1, 5, 70, tmpstr);
    displayCase++;
    break;

  case 149:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 150:
    displayCase++;
    pingDisplay(1);

    break;

  case 151:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 150;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        if (check_TouchBox(156, 189, 70, 100)) // yes
        {
          displayCase = 152;
          parms.write(2, 15);
          parms.write(12, 1);
        }
        if (check_TouchBox(190, 240, 70, 100)) // NO
        {
          displayCase = 152;
          parms.write(2, 0);
          parms.write(12, 1);
        }
      }
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  //***** third question
  case 152:
    // sprintf(tmpstr, "Bug Screen? Y / N");
    sprintf(tmpstr, "Security ?  Y / N");
    showDisplayText(1, 5, 110, tmpstr);
    displayCase++;
    break;

  case 153:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 154:
    displayCase++;
    pingDisplay(1);

    break;

  case 155:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      parseXY();
      displayCase = 154;

      if ((xxx + yyy) >= 0) // got a valid touch
      {
        if (check_TouchBox(156, 189, 110, 140)) // yes
        {
          parmChoice = 8;
          displayForward = 166; // 159;
          displayCase = 60;     // numberpad
          parms.write(12, 1);
        }
        if (check_TouchBox(190, 240, 110, 140)) // NO
        {
          displayCase = 159;
          parms.write(8, 0);
          parms.write(12, 1);
        }
      }
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 159: // outta here
    displayCase = 12;
    break;

  //****** ioExpander Pings v125
  case 200:
    exIndx = 0;
    displayCase++;
    break;

  case 201:
    displayCase++;
    exIndx++;

    if (exIndx > parm[25])
    {
      displayCase = displayForward; // done with io
      if (VB)
        Serial.println("Done with exIO");
    }
    break;

  case 202:
    // make the byte
    i = exRly[((exIndx - 1) * 4) + 0];
    i |= (exRly[((exIndx - 1) * 4) + 1] * 2);
    i |= (exRly[((exIndx - 1) * 4) + 2] * 4);
    i |= (exRly[((exIndx - 1) * 4) + 3] * 8);

    c_send(99, 170 + exIndx, i, 3, exParmIndx + 114);
    displayCase++;
    break;

  case 203:
    sendingDisplayCase = displayCase;
    if (indx >= 16)
      if (comms8Csum(&instr[10], 6, 0) == instr[15]) // csum ok
      {
        displayCase = 201;
        // Serial.print("InByte: ");Serial.println(instr[13]);

        for (i = 0; i < 4; i++) // v518
        {
          exOptoTransition[i] = 0;
          if (exOpto[((exIndx - 1) * 4) + i] == 0) // off last time
            if (bitRead(instr[13], i) != 0)        // on this time
              exOptoTransition[i] = 1;             // set transition

          exOpto[((exIndx - 1) * 4) + i] = bitRead(instr[13], i);
        }

        /* v1518
                  exOpto[((exIndx - 1) * 4) + 0] = bitRead(instr[13], 0);
                  exOpto[((exIndx - 1) * 4) + 1] = bitRead(instr[13], 1);
                  exOpto[((exIndx - 1) * 4) + 2] = bitRead(instr[13], 2);
                  exOpto[((exIndx - 1) * 4) + 3] = bitRead(instr[13], 3);
        */

        i = instr[14]; // get some data via parms
        i *= 8;
        i += instr[12];

        if (exParmIndx < 12) // valid parm number
        {
          exParm[exParmIndx] = i;
        }

        if (++exParmIndx > 11)
          exParmIndx = 0;
      }
      else // bad csum
      {
        Serial.println("Bad CSUM");
        displayCase = 999; // error
      }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 0; // error
      Serial.print("****  Receive Error ioEx: ");
      Serial.print(exIndx);
      Serial.print("  Indx: ");
      Serial.println(indx);
    }
    break;

  //************* Config Lidar parms
  case 300:
    exIndx = 0;
    displayCase++;
    break;

  case 301:
    c_send(6, LIDARADDRESS, parm[55 + exIndx] & 0xff, 2 + exIndx, parm[55 + exIndx] >> 8);
    Serial.printf("Sending local parm# %d (%d)to Lidar parm# %d ->", 55 + exIndx, parm[55 + exIndx], 2 + exIndx);
    displayCase++;
    break;

  case 302:
    sendingDisplayCase = displayCase;
    if (indx >= 16)
      if (comms8Csum(&instr[10], 6, 0) == instr[15]) // csum ok
      {
        Serial.println("OK");
        displayCase = 301;
        exIndx++;
        if (exIndx >= 6)
          displayCase = 303;
      }
      else // bad csum
      {
        Serial.println("Bad CSUM");
        displayCase = 999; // error
      }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 0; // error
      Serial.printf("****  Receive Timeout Lidar: %d Indx: %d\n", exIndx, indx);
    }
    break;

  case 303: // all done
    displayCase = 3;
    break;

    //***************Check Lidar for obstruction

  case 310:
    displayCase++;
    break;

  case 311:
    if (isDoorIdle())
    {
      c_send(1, LIDARADDRESS, 0, 0, 0); // turn off
      Timers[LIDARTIMER] = -1;
    }
    else
    {
      c_send(1, LIDARADDRESS, 75, 0, 0); // turn on
      if (Timers[LIDARTIMER] < 0)
        Timers[LIDARTIMER] = 1;
    }
    displayCase++;
    break;

  case 312:
    sendingDisplayCase = displayCase;
    if (indx >= 16)
      if (comms8Csum(&instr[10], 6, 0) == instr[15]) // csum ok
      {
        // displayCase = 13;
        displayCase = displayForward;

        if (Timers[LIDARTIMER] > 15)
        {
          Serial.println("-");
          if (instr[13] != 0)
          {
            lidarObstruction = 1;
            int ii = instr[14];
            ii << 8;
            ii += instr[12];

            Serial.println("****");

            sprintf(tmpstr, "LIDAR A%d D%d P%d", ii, instr[13], motorGetPosition(0));
            displayText(String(tmpstr));
            Timers[OLEDHOLDTIMER] = 1;

            Serial.println("****");
            displayCase = 313;
          }
        }
      }
      else // bad csum
      {
        Serial.println("Bad CSUM");
        displayCase = 999; // error
      }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 0; // error
      Serial.printf("****  Receive Error Lidar\n");
    }
    break;

  case 313:                           // Lidar stop
    c_send(1, LIDARADDRESS, 0, 0, 0); // turn off
    // Timers[LIDARTIMER] = -1; // remove v530

    displayCase++;
    break;

  case 314:
    if (indx >= 16)
      displayCase = displayForward;

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 0; // error

      Serial.printf("****  Receive Error Lidar\n");
    }
    break;

  case 315: // Lidar learn
    c_send(100, LIDARADDRESS, 75, 0, 10);
    Serial.println("-> Learn Lidar Command");
    lidarLearn = 0;
    displayCase++;
    break;

  case 316:
    if (indx >= 16)
      displayCase = 14;

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 0; // error
      Serial.printf("****  Receive Error Lidar\n");
    }
    break;

    //***************Check slave motor

  case 350:
    displayCase++;
    // break;

  case 351:
    c_send(10, 100, slaveCommand, 0, wiFiOK); //
    if (VB)
      Serial.printf("Slave poll wiFiOK -> %d\n", wiFiOK);
    slaveCommand = 0;
    displayCase++;
    break;

  case 352:
    sendingDisplayCase = displayCase;
    if (indx >= 16)
      if (comms8Csum(&instr[10], 6, 0) == instr[15]) // csum ok
      {
        displayCase = displayForward;
        if (instr[13] != 0) // status coming back from slave
        {
          faultNum = instr[13];
          faultData = ((instr[14] << 8) + instr[12]);
          faultAB = 1; // slave

          Serial.printf("\nSlave Fault = %d, Motor = %d Data = %d\n\n", faultNum, faultAB, faultData);
          displayCase = 360;
        }
        // check response for slave status
      }
      else // bad csum
      {
        Serial.println("Bad Slave CSUM");
        displayCase = 999; // error
      }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 0; // error
      displayText("Slave Timeout");
      delay(2000);
      Serial.printf("****  Receive timeout Slave\n");
    }
    break;

  //****Fault Screens
  case 360: // a little time to clear it out
    displayCase++;
    break;

  case 361:
    displayCase++;
    break;

  case 362:
    Timers[SCREENTIMEOUTTIMER] = 0;
    showDisplayRectangle(displayAddr, 1, 20 + 20, 10, 800 - 80, 87, 1, RED_ADI, 10); // 2-door
    // Serial.println("displayed rectangle");
    displayCase++;
    break;

  case 363:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 364:
    // Serial.println("displayed rectangle reply");
    setTextAttributes(displayAddr, 5, 0, 0, 0, RED_ADI, 0, 0);
    displayCase++;
    break;

  case 365:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 366:
    displayCase++;

    if (faultNum != 0)
    {
      sprintf(tmpstr, "-----"); // v525
      if (faultNum == 1)
        sprintf(tmpstr, "Door #%c PHOTO OBSTRUCTION", faultAB + 65);
      if (faultNum == 2)
        sprintf(tmpstr, "Door #%c M-Fault %2d", faultAB + 65, faultData);
      if (faultNum == 3)
        sprintf(tmpstr, "Door #%c Low Battery %d.%d", faultAB + 65, faultData / 10, faultData % 10);
      if (faultNum == 4)
        sprintf(tmpstr, "Door #%c M-Error %2d", faultAB + 65, faultData);
      if (faultNum == 5)
        sprintf(tmpstr, "Door #%c Lidar Obstruction", faultAB + 65);

      if (attachedDisplays[displayAddr] == 1)
        showDisplayText(displayAddr, 400 - ((strlen(tmpstr) * 13) / 2), 45, tmpstr); // 5" center it
      displayText(tmpstr);
    }

    faultNum = faultData = 0;
    obstructionFault = 0;
    motorFault = 0;
    clearMotorError();
    break;

  case 367:
    // if (countLF >= 2) //got our response
    displayCase++;
    // if (Timers[COMMSREPLYTIMER] > (REPLYTIME * 4)) displayCase = 368;
    break;

  case 368:
    displayCase = displayForward;
    break;

    //*** Show Wifi RSSI

  case 400: // just wrote the logo... wait for response//v534

    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      displayCase++;
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 401:

    Serial.printf("Check Wifi RSSI: %d\n", getWifiRSSI());

    if (parm[130 + displayAddr] >= CLARITY_VER)
    {
      displayCase = displayForward;
      if (attachedDisplays[displayAddr] == 1) // 5"
      {

        if (getWifiRSSI() <= -100)
        {
          showDisplayGraphic(displayAddr, 693, 20, EVOWIFI0, 0, 0);
          Serial.printf("0 Wifi RSSI: %d\n", getWifiRSSI());
          return;
        }

        if (getWifiRSSI() < -90)
        {
          showDisplayGraphic(displayAddr, 708, 10, EVOWIFI1, 0, 0);
          Serial.printf("1 Wifi RSSI: %d\n", getWifiRSSI());
          return;
        }

        if (getWifiRSSI() < -80)
        {
          showDisplayGraphic(displayAddr, 708, 10, EVOWIFI2, 0, 0);
          Serial.printf("2 Wifi RSSI: %d\n", getWifiRSSI());
          return;
        }

        showDisplayGraphic(displayAddr, 708, 10, EVOWIFI3, 0, 0);
        Serial.printf("3 Wifi RSSI: %d\n", getWifiRSSI());
      }

      if (attachedDisplays[displayAddr] == 2) // 2"
      {
        if (getWifiRSSI() <= -100)
        {
          showDisplayGraphic(displayAddr, 200, 5, EVOWIFI0, 0, 0);
          Serial.printf("0 Wifi RSSI: %d\n", getWifiRSSI());
          return;
        }

        if (getWifiRSSI() < -90)
        {
          showDisplayGraphic(displayAddr, 200, 5, EVOWIFI1, 0, 0);
          Serial.printf("1 Wifi RSSI: %d\n", getWifiRSSI());
          return;
        }

        if (getWifiRSSI() < -80)
        {
          showDisplayGraphic(displayAddr, 200, 5, EVOWIFI2, 0, 0);
          Serial.printf("2 Wifi RSSI: %d\n", getWifiRSSI());
          return;
        }

        showDisplayGraphic(displayAddr, 200, 5, EVOWIFI3, 0, 0);
        Serial.printf("3 Wifi RSSI: %d\n", getWifiRSSI());
      }
    }
    else
    {
      Serial.println("Old Clarity Version need to write the text.");
      displayCase++;
    }

    break;

  case 402:
    Serial.println("wifi setting up 402");
    if (attachedDisplays[displayAddr] == 1) // 5"
    {
      showDisplayRectangle(displayAddr, 1, 750, 30, 28, 20, 1, 0, 0);
    }
    else
    {
      showDisplayRectangle(displayAddr, 1, 204, 14, 28, 20, 1, 0, 0);
    }
    displayCase++;
    break;
  case 403:
    Serial.println("wifi setting font 403");
    if (countLF >= 2)
    {
      setTextAttributes(displayAddr, 5, 0, 0, 0, 0, TEAL_ADI, 0);
      displayCase++;
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 404:
    rssiWIFI = getWifiRSSI();
    if (rssiWIFI <= -100)
      rssiWIFI = 0;
    else if (rssiWIFI <= -90)
      rssiWIFI = 1;
    else if (rssiWIFI <= -80)
      rssiWIFI = 2;
    else
      rssiWIFI = 3;
    Serial.println("wifi setting drawing wifi status 404");
    if (countLF >= 2) // got our response
    {
      displayCase = displayForward;
      sprintf(tmpstr, "W%d", rssiWIFI);
      Serial.printf("WIFI String: %s\r\n", tmpstr);
      if (attachedDisplays[displayAddr] == 1) // 5"
      {
        showDisplayText(displayAddr, 752, 30, tmpstr);
      }
      else
      {
        showDisplayText(displayAddr, 205, 15, tmpstr);
      }
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 999; // too long
    }
    break;

  case 410: // v534
    showDisplayGraphic(displayAddr, 0, 0, CLARITYOKSCREEN, 0, 0);
    displayCase++;
    break;

  case 411: // v534
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      displayCase = 22; // go wait for a touch
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

    break;

  case 420:
    fillDisplay(displayAddr, BLACK_ADI);
    displayCase++;
    break;

  case 421:
    if (countLF >= 2) // got our response
    {
      displayCase++;
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long

    break;

  case 422:
    if (attachedDisplays[displayAddr] == 1) // 5"
    {
      setTextAttributes(displayAddr, 7, 0, 0, 0, 0, TEAL_ADI, 0);
    }
    else
    {
      setTextAttributes(displayAddr, 5, 0, 0, 0, 0, TEAL_ADI, 0);
    }
    displayCase++;
    break;

  case 423:
    if (countLF >= 2) // got our response
    {
      if (attachedDisplays[displayAddr] == 1) // 5"
      {
        showDisplayText(displayAddr, 344, 210, "Clarity");
      }
      else
      {
        showDisplayText(displayAddr, 78, 100, "Clarity");
      }
      displayCase++;
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 999; // too long
    }
    break;

  case 424:
    if (countLF >= 2) // got our response
    {
      if (attachedDisplays[displayAddr] == 1) // 5"
      {
        showDisplayText(displayAddr, 296, 244, "Wifi Enabled!");
      }
      else
      {
        showDisplayText(displayAddr, 42, 124, "Wifi Enabled!");
      }
      displayCase++;
    }
  case 425:
    if (countLF >= 2) // got our response
    {
      displayCase = 22;
    }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 999; // too long
    }
    break;

  //***** find multiple displays
  case 600:
    Timers[SCREENTIMEOUTTIMER] = 0;
    fillDisplay(1, BLACK_ADI);
    // delay(200);
    displayCase++;
    break;

  case 601:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 999; // too long
      qualifyFault(36, 0);
    }
    break;

  case 602:
    restoreFault(36);
    setTextAttributes(1, 5, 0, 0, 0, 0, TEAL_ADI, 0);
    displayCase++;
    break;

  case 603:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase = 610;
    displayAddr = 1;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 610:
    statusDisplay(displayAddr); // ping
    displayCase++;
    break;

  case 611:

    if (countLF >= 2) // got our response
    {
      parseStatus();
      displayCase++;

      if (xxx > 330) // 5"
      {
        // X_OFFSET = 530;
        // Y_OFFSET = 95;
        attachedDisplays[displayAddr] = 1;
        Serial.print(displayAddr);
        Serial.println(": Found 5 inch Display");
      }
      else // 2.4"
      {
        // X_OFFSET = 0
        // Y_OFFSET = 0;
        attachedDisplays[displayAddr] = 2;
        Serial.print(displayAddr);
        Serial.println(": Found 2.4 inch Display");
      }
    }
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME / 6))
      displayCase++; // too long
    break;

  case 612:
    switch (attachedDisplays[displayAddr])
    {
    case 1: // 5"
      sprintf(tmpstr, "%d 5 inch    v%d", displayAddr, zzz);
      parms.write(130 + displayAddr, zzz);
      break;
    case 2: // 2.4"

      sprintf(tmpstr, "%d 2.4 inch  v%d", displayAddr, zzz);
      parms.write(130 + displayAddr, zzz);
      break;
    default: // no display
      sprintf(tmpstr, "%d No Display", displayAddr);
      break;
    }

    displayText(String(tmpstr)); // Oled

    showDisplayText(1, 5, (displayAddr * 40), tmpstr);
    displayCase++;
    break;

  case 613:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 614:
    if (++displayAddr > 5) // we done scanning
    {
      displayCase = 1;
      displayAddr = 1;
    }
    else
      displayCase = 610;
    break;

  //****Obstruction Screen
  case 710: // a little time to clear it out
    obstructionFault = 0;
    Timers[COMMSREPLYTIMER] = 0;
    displayCase++;

  case 711:
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME))
      displayCase++;
    break;

  case 712:
    Timers[SCREENTIMEOUTTIMER] = 0;
    fillDisplay(displayAddr, RED_ADI);
    // delay(200);
    displayCase++;
    break;

  case 713:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 714:
    setTextAttributes(displayAddr, 5, 0, 0, 0, RED_ADI, 0, 0);
    displayCase++;
    break;

  case 715:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 716:
    sprintf(tmpstr, "OBSTRUCTION");
    // showDisplayText(displayAddr, 35, 30, tmpstr);
    if (attachedDisplays[displayAddr] == 2)
      showDisplayText(displayAddr, (240 - (11 * 14)) / 2, 30, tmpstr); // 2"  11 chars, 14 pixels each,  centered
    if (attachedDisplays[displayAddr] == 1)
      showDisplayText(displayAddr, (800 - (11 * 14)) / 2, 30, tmpstr); // 5"
    displayCase++;
    break;

  case 717:
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME * 3))
      displayCase = 19;
    break;

  //****Motor Fault Screen
  case 810: // a little time to clear it out
  {
    motorStop();
    Serial.println("MotorStop 810");
    Timers[COMMSREPLYTIMER] = 0;
    displayCase++;
  }
  case 811:
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME))
      displayCase++;
    break;

  case 812:
    Timers[SCREENTIMEOUTTIMER] = 0;
    fillDisplay(displayAddr, RED_ADI);
    // delay(200);
    displayCase++;
    break;

  case 813:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 814:
    setTextAttributes(displayAddr, 5, 0, 0, 0, RED_ADI, 0, 0);
    displayCase++;
    break;

  case 815:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 816:

    if (motorFault) // 9999 fault
    {
      sprintf(tmpstr, "Motor Fault %2d", motorFault);
      faultNum = 2;
      faultAB = 0;
      faultData = motorFault;
    }
    else
    {
      sprintf(tmpstr, "Motor Error %2d", getMotorError());
      faultNum = 4;
      faultAB = 0;
      faultData = getMotorError();
    }

    // showDisplayText(displayAddr, 35, 30, tmpstr);
    if (attachedDisplays[displayAddr] == 2)
      showDisplayText(displayAddr, (240 - (14 * 14)) / 2, 30, tmpstr); // 2"  13 chars, 14 pixels each,  centered
    if (attachedDisplays[displayAddr] == 1)
      showDisplayText(displayAddr, (800 - (14 * 14)) / 2, 30, tmpstr); // 5"

    displayCase++;
    motorFault = 0;
    clearMotorError();

    cDOOROPEN = 0;
    cDOORCLOSE = 0;
    cDOORSTOP = 1;

    motorStop();

    logicCase = 0;
    break;

  case 817:
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME * 5))
      displayCase = 9;
    break;

  //****Battery Fault Screen    //V120
  case 910: // a little time to clear it out
  {
    Timers[COMMSREPLYTIMER] = 0;
    cDOOROPEN = 0;
    cDOORCLOSE = 0;
    cDOORSTOP = 1;
    SCREENCLOSE = SCREENOPEN = SCREENSTOP = 0;
    displayCase++;
  }

  case 911:
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME))
      displayCase++;
    break;

  case 912:
    Timers[SCREENTIMEOUTTIMER] = 0;
    fillDisplay(displayAddr, LT_YELLOW_ADI);
    // delay(200);
    displayCase++;
    break;

  case 913:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 914:
    setTextAttributes(displayAddr, 5, 0, 0, 0, LT_YELLOW_ADI, BLACK_ADI, 0);
    displayCase++;
    break;

  case 915:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 916:
    battV = (int)(voltageNow);
    sprintf(tmpstr, "Battery Fault:");
    if (attachedDisplays[displayAddr] == 2)
      showDisplayText(displayAddr, (240 - (14 * 14)) / 2, 100, tmpstr); // 2"  13 chars, 14 pixels each,  centered
    if (attachedDisplays[displayAddr] == 1)
      showDisplayText(displayAddr, (800 - (14 * 14)) / 2, 200, tmpstr); // 5"
    displayCase++;
    // batteryFault = 0;
    break;

  case 917:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
      displayCase++;
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  case 918: // display second line
    sprintf(tmpstr, "%2d.%d", battV / 10, battV % 10);
    if (attachedDisplays[displayAddr] == 2)
      showDisplayText(displayAddr, (240 - (4 * 14)) / 2, 130, tmpstr); // 2"  4 chars, 14 pixels each,  centered
    if (attachedDisplays[displayAddr] == 1)
      showDisplayText(displayAddr, (800 - (4 * 14)) / 2, 230, tmpstr); // 5"
    displayCase++;
    break;

  case 919:
    sendingDisplayCase = displayCase;
    if (Timers[COMMSREPLYTIMER] > (REPLYTIME * 5))
      displayCase = 999;
    break;

  case 999:
    sprintf(tmpstr, "CTimeout Addr: %d,%d", displayAddr, sendingDisplayCase);
    displayText(String(tmpstr));
    Timers[SCREENTIMEOUTTIMER] = 0;
    displayCase = 0;
    break;

    //***************Test Lidar for obstruction

  case 1310:
    displayCase++;
    lidarTest = 0;
    break;

  case 1311:

    c_send(1, LIDARADDRESS, 75, 0, 0); // turn on
    if (Timers[LIDARTIMER] < 0)
      Timers[LIDARTIMER] = 0;
    Timers[SCREENTIMEOUTTIMER] = 0; // v520
    displayCase++;
    break;

  case 1312:
    sendingDisplayCase = displayCase;
    if (indx >= 16)
      if (comms8Csum(&instr[10], 6, 0) == instr[15]) // csum ok
      {
        displayCase = 1310;
        // displayCase = displayForward;

        if (Timers[LIDARTIMER] > 15)
        {
          Serial.println("-");
          if (instr[13] != 0)
          {
            // lidarObstruction = 1;
            int ii = instr[14];
            ii <<= 8;
            ii += instr[12];

            sprintf(tmpstr, "LIDAR A%d, D%d", ii, instr[13]);
            displayText(String(tmpstr));

            displayCase = 1313;
          }
        }
      }
      else // bad csum
      {
        Serial.println("Bad CSUM");
        displayCase = 999; // error
      }

    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
    {
      displayCase = 0; // error
      Serial.printf("****  Receive Timeout Lidar\n");
    }
    break;

  case 1313:
    displayCase++;
    beepDisplay(1);
    break;

  case 1314:
    sendingDisplayCase = displayCase;
    if (countLF >= 2) // got our response
    {
      displayCase = 1310;
    }
    if (Timers[COMMSREPLYTIMER] > REPLYTIME)
      displayCase = 999; // too long
    break;

  default:
    Serial.print("*************************************Bad displayCase: ");
    Serial.println(displayCase);
    displayCase = 0;
    break;
  }

  if (VB)
    if (lastDisplayCase != displayCase)
    {
      Serial.printf("DCase: %d DAdr: %d\n", displayCase, displayAddr);
      lastDisplayCase = displayCase;
    }
}

int check_TouchBox(int x1, int x2, int y1, int y2)
{
  if ((xxx > x1) &
      (xxx < x2) &
      (yyy > y1) &
      (yyy < y2))
    return (1);

  return (0);
}

int parseXY(void)
{
  char tmpstr[40];
  int p;
  int i;
  int strt, csum, stp;

  for (i = 0; instr[i] != LF; i++)
    ;  // find first LF from echo
  i++; // should be pointing to <ESC>
  strt = i;

  i++;                //"T"
  if (instr[i] != 84) // is it a T? , if not, go to LOGO screen  //v523
  {
    Serial.printf("Awaiting Command on display\n");
    displayCase = 20;
    xxx = -1;
    yyy = -1;
    return (0);
  }

  i++; //","

  i++; // first data
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));

  // for (; instr[i] != 44; i++) tmpstr[p++] = instr[i]; //build string
  for (; instr[i] != 44; i++)
  {
    if (p < sizeof(tmpstr))
      tmpstr[p++] = instr[i]; // build string
  }

  tmpstr[++p] = 0; // terminate it
  address = atoi(tmpstr);

  i++; // second data
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  // for (; instr[i] != 44; i++) tmpstr[p++] = instr[i]; //build string
  for (; instr[i] != 44; i++)
  {
    if (p < sizeof(tmpstr))
      tmpstr[p++] = instr[i]; // build string
  }
  tmpstr[++p] = 0; // terminate it
  xxx = atoi(tmpstr);

  i++; // third data
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  // for (; instr[i] != 44; i++) tmpstr[p++] = instr[i]; //build string
  for (; instr[i] != 44; i++)
  {
    if (p < sizeof(tmpstr))
      tmpstr[p++] = instr[i]; // build string
  }
  stp = i;
  tmpstr[++p] = 0; // terminate it
  yyy = atoi(tmpstr);

  i++; // csum
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  // for (; instr[i] != LF; i++) tmpstr[p++] = instr[i]; //build string
  for (; instr[i] != LF; i++)
  {
    if (p < sizeof(tmpstr))
      tmpstr[p++] = instr[i]; // build string
  }

  tmpstr[++p] = 0; // terminate it
  csum = atoi(tmpstr);

  p = 0;
  for (i = strt; i <= stp; i++)
  {
    // Serial.print(instr[i]);
    p += instr[i];
  }

  p = p & 0xff;
  p = ((0 - p) & 0xff); // 2's comp
  if (p == 255)
    p = 0; // adjust it if 255

  if (p != csum) // bad csum
  {
    xxx = -1;
    yyy = -1;
    Serial.println("Bad RX checksum");
    return (0);
  }

  if ((xxx + yyy) >= 0) // got a valid touch
  {
    sprintf(tmpstr, "--------X: %d, Y: %d : %d", xxx, yyy, cDOORCLOSE);
    Serial.println(tmpstr);

    Timers[SCREENTIMEOUTTIMER] = 0;
  }
  return (1);
}

int parseStatus(void)
{
  char tmpstr[40];
  int p;
  int i;
  int strt, csum, stp;

  for (i = 0; instr[i] != LF; i++)
    ;  // find first LF from echo
  i++; // should be pointing to <ESC>
  strt = i;

  i++; //"$"
  i++; //","

  i++; // first data
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  for (; instr[i] != 44; i++)
    tmpstr[p++] = instr[i]; // build string
  tmpstr[++p] = 0;          // terminate it
  address = atoi(tmpstr);

  i++; // second data
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  for (; instr[i] != 44; i++)
    tmpstr[p++] = instr[i]; // build string
  tmpstr[++p] = 0;          // terminate it
  xxx = atoi(tmpstr);

  i++; // third data
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  for (; instr[i] != 44; i++)
    tmpstr[p++] = instr[i]; // build string
  // stp = i;
  tmpstr[++p] = 0; // terminate it
  yyy = atoi(tmpstr);

  i++; // forth data
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  for (; instr[i] != 44; i++)
    tmpstr[p++] = instr[i]; // build string
  stp = i;
  tmpstr[++p] = 0; // terminate it
  zzz = atoi(tmpstr);

  i++; // csum
  p = 0;
  memset(tmpstr, 0, sizeof(tmpstr));
  for (; instr[i] != LF; i++)
    tmpstr[p++] = instr[i]; // build string
  tmpstr[++p] = 0;          // terminate it
  csum = atoi(tmpstr);

  p = 0;
  for (i = strt; i <= stp; i++)
  {
    // Serial.print(instr[i]);
    p += instr[i];
  }

  p = p & 0xff;
  p = ((0 - p) & 0xff); // 2's comp
  if (p == 255)
    p = 0; // adjust it if 255

  if (p != csum) // bad csum
  {
    xxx = -1;
    yyy = -1;
    zzz = -1;
    Serial.println("Bad Status RX checksum");
    return (0);
  }

  sprintf(tmpstr, "X=%d,Y=%d,Z=%d", xxx, yyy, zzz);
  Serial.println(tmpstr);

  return (1);
}

int currentDisplayCase(void)
{
  return (displayCase);
}

int LowBattery(void)
{
  int vbatt = 0;

  if (parm[10] == 0)
    return (0); // turned off

  vbatt = voltageNow;
  // Serial.print("vbatt: "); Serial.println(vbatt);

  if (vbatt < (parm[10]))
  {
    // Serial.print("vbatt: "); Serial.println(vbatt);
    // batteryFault = 1;
    faultNum = 3;
    faultData = (int)(voltageNow);
    faultAB = 0;
    return (1); // battery voltage low
  }
  return (0);
}

void c_send(

    unsigned char cmd,
    unsigned char addr,
    unsigned char dat1,
    unsigned char parm,
    unsigned char dat2)
{
  unsigned char cc;

  Timers[COMMSREPLYTIMER] = 0; // start it
  indx = 0;
  memset(instr, 0, sizeof(instr));

  cc = cmd;
  cc += addr;
  cc += dat1;
  cc += parm;
  cc += dat2;
  if (cc == 0xff)
    cc = 0; /* if checksum is ff then make it 0 */
  Serial2.write(0xff);
  Serial2.write(0xff);
  Serial2.write(cmd);
  Serial2.write(addr);
  Serial2.write(dat1);
  Serial2.write(parm);
  Serial2.write(dat2);
  Serial2.write(cc);

  if (VB)
  {
    Serial.print("0xff,");
    Serial.print("0xff,");

    Serial.print(cmd);
    Serial.print(":");
    Serial.print(addr);
    Serial.print(":");
    Serial.print(dat1);
    Serial.print(":");
    Serial.print(parm);
    Serial.print(":");
    Serial.print(dat2);
    Serial.print(":");
    Serial.println(cc);
  }
}

byte comms8Csum(byte *buffer, byte len, bool tc)
{
  byte i, csum;

  // buffer += 2;
  csum = 0;
  for (i = 0; i < (len - 1); i++)
    csum += *buffer++;

  if (tc)
    csum = 0 - csum;

  if (csum == 0xff)
    csum = 0;
  return (csum);
}

int checkLidarObstruction(void)
{
  return (lidarObstruction);
}

void clearLidarObstruction(void)
{
  lidarObstruction = 0;
}

void extParmWrite(byte addr, uint32_t pnum, uint32_t dta)
{
  byte extstr[32];
  int i;

  indx = 0;
  countLF = 0;
  Timers[COMMSREPLYTIMER] = 0; // restart it
  memset(extstr, 0, sizeof(extstr));
  memset(instr, 0, sizeof(instr));

  extstr[0] = 0xff;
  extstr[1] = 0xff;
  extstr[2] = 106;
  extstr[3] = addr;
  extstr[4] = pnum & 0xff;
  extstr[6] = (pnum >> 8) & 0xff;
  extstr[7] = comms8Csum(&extstr[2], 5, 0);
  extstr[16] = dta & 0xff;
  extstr[18] = (dta >> 8) & 0xff;
  extstr[20] = (dta >> 16) & 0xff;
  extstr[22] = (dta >> 24) & 0xff;
  extstr[27] = comms8Csum(&extstr[2], 25, 1);

  Serial.print("Remote Parm request: ");

  for (i = 0; i < 28; i++) // sent it
  {
    Serial2.write(extstr[i]);
    Serial.print(extstr[i]);
    if (i != 27)
      Serial.print(": ");
  }

  Serial.println();
}

/*
  struct ioEx  //**********************
  {

  uint16_t relay[4];
  uint16_t opto[4];
  uint16_t Hz[4];
  uint16_t a2d[8];

  }ioExpander[5];


  ioExpander[0].relay[0] = 1;  //***********************

  267. 538

  35
  parms
  84

  114
  LEARN OPEN
  209

  238
  LEARN CLOSE
  289

  320
  SCREEN PGM
  369

  400
  EXIT
  453

*/
/*
  105 32 bit Parm Read
  Command
  0 1 2 3 4 5 6 7
  FF FF 105 248 P0 0 P1 CS1
  Reply
  0 1 2 3 4 5 6 7
  FF FF 105 0 P0 0 P1 CS1
  Extended Reply
  8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
  A0 105 A1 0 A2 0 A3 0 D0 0 D1 0 D2 0 D3 P1 P0 0 0 CS2
*/
