#define info0  "EMC4 Build-01  ZDRV "
#define info1  "F/W Z0325-C01_v0.15 " 
#define info2  "Assy: CES180325-3   "
#define info3  "Date: 09/19/2018    "
/* 
 * Z0325-C01_v0.15 9/19/18 --------------------------------
 *  Ready for Acceptance Test
 *    Pending LCD Display I2C Address updater
 *    Pending Zscale Calibration
 *  Program 74%, Memory 55%
 *    RUN  =ok=
 *    INF  =ok=
 *    TEST =ok=
 *    RNG  =ok=
 *    CAL  =ok=
 * --------------------------------------------------------
 * LCD 2004 I2C (5V)
 *    A5 = SCL
 *    A4 = SDA
 * --------------------------------------------------------
 *    Pro MiniPORT Assignments
 *      A7  Z.Speed Zclk Setting
 *      A6  
 *      A5/D19  I2C-SCL
 *      A4/D18  I2C-SDA
 * -----------------------     
 *      A3/D17  PReady Input PWMGen.D12 Interface (LO=NotReady)
 *      A2/D16  ESense Input (End Sense)
 *      A1/D15  BSense Input (Bottom Sense)  
 *      A0/D14  TSense Input (Top Sense)
 *      D13 Output Zdir ZDRV-DIR (IDE Reserved Output Only)
 *      D12 Output Zclk ZDRV-CLK
 *      D11 Output Zen ZDRV-EN 
 *      D10 Output EDM-ON
 *      D9  Input Key Zdn 
 *      D8  Input Key Zup 
 *      D7  Input Key Zrun 
 *      D6  Input Key Zset 
 *      D5  Input Key Page 
 *      D4  Output RUNspeed
 *      D3  Input ISense 
 *      D2  Input PSense 
 * -----------------------     
 *      D1  RXD System Reserved
 *      D0  TXD System Reserved
 *
 * === Pro Mini w/ 5x1 Extended Header =========================
 *  (D9)(D8)(D7)(D6)(D5)(D4)(D3)(D2)(GND)(RES)(RXD)(TXD) < DTR >
 *      (GND) *                                          < RX >
 *         A4 *                                          < TX >
 *         A5 *                                          < VCC >
 *         A6 *                                          < CTS >
 *         A7 *                                          < GND >
 *  (D10)(D11)(D12)(D13)(A0)(A1)(A2)(A3)(VCC)(RES)(GND)(RAW) 
 *  
 * === TB6560 Stepper Driver ===================================
 *    A+ (Red)
 *    A- (Blue)
 *    B+ (Green)
 *    B- (Black)
 *    Note: New Z Axal B+/B- Swapped
 *    
 * === TB6560 SW Settings ======================================
 *  --- Running Current --- (1.5A*)
 *  sw1 OFF OFF OFF OFF OFF ON  OFF ON*  ON  ON  ON  ON  ON  ON
 *  sw2 OFF OFF ON  ON  ON  OFF ON  OFF* OFF ON  OFF ON  ON  ON
 *  sw3 ON  ON  OFF OFF ON  OFF ON  ON*  OFF OFF ON  ON  OFF ON
 *  S1  ON  OFF ON  OFF ON  ON  OFF ON*  OFF ON  OFF ON  OFF OFF
 *      0.3 0.5 0.8  1  1.1 1.2 1.4 1.5* 1.6 1.9  2  2.2 2.6 3.0
 *  --- Stop Current --- (50%)
 *  S2  OFF*  ON
 *      50%*  20%
 *  --- Excitation Mode --- (Mode 16)
 *  S3  OFF ON  ON* OFF (CPU Controls Pin, OFF=HiRES to Commom+5V ,Set to OFF )
 *  S4  OFF OFF ON* ON (Set to ON for CPU Controls)
 *       1  2   8*  16
 *    CPU Controls S3, S4=ON Default,  FAST=ON(8)  HiRES=OFF(16) <-- Use This One
 *    CPU Controls S3, S4=OFF Default, FAST=OFF(1) HiRES=OFF(2)
 *    CPU Controls S4, S3=ON Default,  FAST=OFF(2) HiRES=ON(8)
 *    CPU Controls S4, S3=OFF Default, FAST=OFF(1) HiRES=ON(16)
 *  --- Decay Setting ---
 *  S5  OFF* OFF ON  ON
 *  S6  OFF* ON  OFF ON
 *      0%*  25% 50% 100%
 *  ZScale per Inch Excitation Mode Note: 
 *    Tested on Z60mm
 *  Mode.1 3off 4off = Not Used
 *  Mode.2 3on 4off  = Normal = 2546 (0.0003928) 
 *  Mode.8 3on 4on   = fine  = 10172 (0.0000983) as Fast
 *  Mode.16 3off 4on = Ultra = 20318 (0.0000492) as HiRES
 * --- Program Settings ---
 *    Default S3=OFF S4=ON
 *    TB6560 GND = SystemGND
 *    D4=S3- LO=(S3=ON)FAST HI=(S3=OFF)HiRES
 * =============================================================
 */

// LCD 2004A with I2C
  #include <FastIO.h>
  #include <I2CIO.h>
  #include <LiquidCrystal_I2C.h>
  // the pins on the I2C chip used for LCD connections:
  //                   addr, en,rw,rs,d4,d5,d6,d7,bl,blpol
  //LiquidCrystal_I2C lcd2(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // 2004A LCD
  LiquidCrystal_I2C lcd2(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // 2004A LCD
  
// Z-drive Stepper Output Port
  #define Zclk 12 //ZDRV Clock output
    #define ZclkH HIGH
    #define ZclkL LOW
  #define Zdir 13 //ZDRV Direction output 
    #define ZdirUP HIGH
    #define ZdirDN LOW
  #define Zen 11 //ZDRV Enable output
    #define Zon LOW
    #define Zoff HIGH
  #define EDM 10 //EDM Enable output
    #define EDMon HIGH
    #define EDMoff LOW

// Sense Input Port
  #define TSense 14 //TOP Limit     
  #define BSense 15 //BOT Limit
  #define ESense 16 //End Detect
  #define PSense 2 //Probe input  
  #define ISense 3 //Probe Current  

// PWMGen Interface
  #define PWMGen 17 //PWMGen Interface Input Port
  #define NotReady LOW
  
// Z-Drive working variable
  long Zcount; //Current Step Count (from Work Zero)
  long ZcountSet; //Target Step Count (from Work Zero)
  long ZcountSave; //+UP -DN Temp Display Counts (from Work Zero)
  long ZcountEnd; //Finish Depth (from Work Zero)
  long ZcountPark;  //Part in Counts (from Work Zero)
  long ZcountProx;  //Prox in Counts (from Work Zero)
  long ZcountGap;  //Gap in Counts (from Work Zero)
  long ZcountMax; //Top Limit Counts (from Work Zero)
  long ZcountMin; //Bottom Limit Counts (from Work Zero)
  long ZcountZero; //Z Machine Work Zero offset  
  long ZcountMMax; //Z Machine Top Limit TSense
  long ZcountMMin; //Z Machine Bot Limit BSense (BOT = Machine-Zero)
  long ZcountCHK; // Z Temp storage for Error Checking

// Z-Drive from Work Zero
  float ZmaxDefault = 2.1; //Z Top 2.1 Inch (60mm Axis) - Justify Sensing Block Thk.
  float Zmax = 2.1; // Z Top in Inch
  float Zmin = 0; // Machine Zero Default = 0
  float Zprox = 0.05; //Prox Zone in Inch from Zero
  float Zend = -0.1; //Job Finish Depth in Inch from Zero (initial value)
  float Zgap = 0.02; //Max gap prior Prox Clearing in Inch from Zero
  float Zzero;  // Zero in Inch
  #define Zcalblock 1.0 //CAL Prode DIA or Thickness 1-inch default

// Z-Drive Default Value 
  //Mode.8 3on 4on   = fine  = 10160 (0.0000983) as FAST
  //Mode.16 3off 4on = Ultra = 20320 (0.0000491) as HiRES
  long Zscale;  //steps counts per Inch
  long Zscalefast = 10160;  //steps counts per Inch (TB6560 Mode8 S3=ON S4=ON) 60mm FAST
  long Zscalehires = 20320;  //steps counts per Inch (TB6560 Mode16 S3=OFF S4=ON) 60mm HiRES 

// Z-drive speed control 
  #define Zs3sw 4  //Z S3 Speed Select port
  #define Zfast LOW  //S3=ON
  #define Zhires HIGH  //S3=OFF

// Z-Drive Operating Range Limits
  #define ZscaleMin 800 //counts
  #define ZscaleMax 30000 //counts

// Z-Drive Pulse 10KHz=100uS 20KHz=50uS
  #define A7pulse A7  //POT Speed Adj
  #define pulseMin 35 //Maximum Speed, Minimum ON-Time in uS
  #define A7comp 16  //estimated CPU Execution compansation in uS
  long Ain7; //Read A7 Input 0-255 for WORK Zclk
  
// System control Keys
  #define kdn 9 //Z Down Key
  #define kup 8 //Z up Key
  #define krun 7 //Z Run Key
  #define kset 6 //Z stop Key
  #define kpage 5  //Z Page key
  
// Processing Steps Index
  byte Stest;  //Test/Monitor process steps 
  byte Srng;  //Range Finder process steps
  byte Scal;  //Calibration process steps 
  byte Sset; //Setup process steps
  byte Srun; //Run process steps  
  byte Sinf; //INF process steps

  byte Zclear;  //Test if Not Clear Discharging Gap
  byte Zstop; // 0=RUN

// Z Progress Msg place in Program Segment
  #include <avr/pgmspace.h>
  const char string_0[]  PROGMEM = "[RUN] key to Start";   
  const char string_1[]  PROGMEM = "[S]Change [R]Next ";   
  const char string_2[]  PROGMEM = "[S]Select [R]Start";   
  const char string_3[]  PROGMEM = "JOB Finished.     ";   
  const char string_4[]  PROGMEM = "to Start Position ";   
  const char string_5[]  PROGMEM = "Job in Progress...";   
  const char string_6[]  PROGMEM = "PAUSE [RUN]resume ";   
  const char string_7[]  PROGMEM = "Job Cancel [R]next";   
  const char string_8[]  PROGMEM = "Z-Bottom Reached. ";   
  const char string_9[]  PROGMEM = "Cleaning...       ";   
  const char string_10[] PROGMEM = "goto TOP Position ";   
  const char string_11[] PROGMEM = "goto BOT Position ";   
  const char string_12[] PROGMEM = "goto MID Position ";   
  const char string_13[] PROGMEM = "Range Setup DONE  ";   
  const char string_14[] PROGMEM = "[S]Save [RUN]Skip ";   
  const char string_15[] PROGMEM = "New Value Saved   ";   
  const char string_16[] PROGMEM = "No Change, Skip   ";   
  const char string_17[] PROGMEM = "seek DN Base Ref0 ";   
  const char string_18[] PROGMEM = "seek DN 1-inch BLK";   
  const char string_19[] PROGMEM = "Calibration DONE  ";   
  const char string_20[] PROGMEM = "Detect Work ZERO  ";  
  const char string_21[] PROGMEM = "SysCheck NotReady!";  
  const char string_22[] PROGMEM = "[S]Cancel [R]Next ";   

// Z Progress Msg strings define
  const char* const Zprogress[] PROGMEM = {
    string_0, string_1, string_2, string_3, string_4, 
    string_5, string_6, string_7, string_8, string_9, 
    string_10, string_11, string_12, string_13, string_14, 
    string_15, string_16, string_17, string_18, string_19,
    string_20, string_21, string_22 };
  char buffer[22];    // make sure this is large enough for the largest string it must hold

// Z-drive RUN Mode
  byte Zedm;  // 0=FAST, 1=HiRES, 3=AUTO
  #define mFAST 0
  #define mHiRES 1
  #define mAUTO 2
  char* Zedmmsg[]={ //6-chr
    " FAST ", //0 No Read-Out, Finish at ESense, BSense
    " HiRES", //1 W/ Readout, Finish at ESense, BSense
    " AUTO "}; //2

// Zpage Page MSG 2004 20 chr
  byte Zpage = 0; //default
  #define firstpage 0
  #define RUN 0   //=ok=
  #define INF 1   //=ok=
  #define TEST 2  //=ok=
  #define RNG 3   //=ok=
  #define CAL 4   //=ok=
  #define lastpage 4
  char* Zpagemsg[]={  //6-chr
    "<RUN> ", //0
    "INFO> ", //1
    "TEST> ", //2
    "RANGE ", //3
    "<CAL> "}; //4

// Zmode Status Line MSG 2004 20 chr
  byte Zmode = 0; //Mode state
    #define mUP 1
    #define mDN 2
    #define mPause 3
    #define mError 4
  char* Zmodemsg[]={  // 8-chr
    " ------ ",  //0 
    " (z.UP) ",  //1 Zdir
    " (z.DN) ",  //2 Zdir
    " PAUSE! ",  //3 Zstop=1
    " ERROR! ",  //4 ?
    " <<  >> "}; //5 Srun=2

// System I/O & Variable
  #define disptime 2000 //delay for Display in mS
  #define keydebounce 250 //delay for keydebounce
  #define holdindex 30  //Key refresh count before switch to high speed 
  byte pagemsg; //Init Display MSG once
  String mfg; //Long MSG
  String mfgtemp; //Long MSG temp
  unsigned long msNow; //Current Wait Timer
  unsigned long msLast; //Last Wait Timer
  unsigned long msWait = 1000; //default 1-second Wait
  float Zdata;  //common shares data
  int kupHold; //UP long press
  int kdnHold; //DN long press
  int keyHold; //any long press
  byte AltDisp;  //Alternate Display Toggle Flag
    
/* ------------------------------------------------------------------------*/
/// Setup Start Here
void setup() {

  //Stepper Drive Controls Output
  pinMode(EDM,OUTPUT); digitalWrite(EDM,EDMoff);
  pinMode(Zen,OUTPUT); digitalWrite(Zen,Zoff);
  pinMode(Zdir,OUTPUT); digitalWrite(Zdir,ZdirUP);
  pinMode(Zclk,OUTPUT); digitalWrite(Zclk,ZclkL);
  pinMode(Zs3sw,OUTPUT); digitalWrite(Zs3sw,Zfast);

  //ZDRV Sensing input
  pinMode(TSense,INPUT_PULLUP);
  pinMode(BSense,INPUT_PULLUP);
  pinMode(ESense,INPUT_PULLUP);
  pinMode(PSense,INPUT_PULLUP);
  pinMode(ISense,INPUT_PULLUP);

  // Key Input
  pinMode(kdn,INPUT_PULLUP);
  pinMode(kup,INPUT_PULLUP);
  pinMode(krun,INPUT_PULLUP);
  pinMode(kset,INPUT_PULLUP);
  pinMode(kpage,INPUT_PULLUP);

  // PWMGen Interface Input
  pinMode(PWMGen,INPUT_PULLUP);
  
  // Load Z Setup Default
  Zdefault();
  
  // Update Z Data
  Zupdate();
  
  // Enable Z-Driver
  digitalWrite(Zen,Zon);

  //LCD setup
  lcd2.begin(20, 4);

  // Display Info
  mfg = (info0); printmfg(0,0);
  mfg = (info1); printmfg(0,1);
  mfg = (info2); printmfg(0,2);
  mfg = (info3); printmfg(0,3);
  delay(disptime*2);

  // StartUp Page
  Zpage = RUN;

}
/// Setup End Here
/* ------------------------------------------------------------------------*/
/*  P r o g r a m   S t a r t   H e r e
/* ------------------------------------------------------------------------*/
void loop() {

  // Check Zstop Status
  //if (Zstop == 3) Zmode = mPause;
  //if (Zstop == 4) Zmode = mError;

  // Move Z if not in RUN process
  if ((Srun == 0) && (Zstop == 0)) Z2countSet();  

  // Reset non-active Process Index while idling
  if (Zpage!=TEST) Stest=0;  
  if (Zpage!=RUN) Srun=0;
  if (Zpage!=CAL) Scal=0;
  if (Zpage!=RNG) Srng=0;
  if (Zpage!=INF) Sinf=0; 
  
  // kpage Key Check --------------
  if (digitalRead(kpage)==LOW) {
    Zpage++;
    if (Zpage>lastpage) Zpage=firstpage;
    delay(keydebounce); // debounce
  }

  // kup Key Check --------------
  if ((digitalRead(kup)==LOW) && (Zstop==0) && (Zpage!=TEST)) { 
    if ((digitalRead(TSense)==HIGH)) {
      kupHold++; ZcountSet = ZcountSet + kupHold;
      if (kupHold>holdindex) {
        ZmUP(); while ((digitalRead(TSense)==HIGH) && (digitalRead(kup)==LOW)) { ZmoveUP1(); ZcountSet=Zcount; } 
      }
    } else { 
      do {} while ((digitalRead(kup)==LOW));
    }
  }  else { kupHold = 0; }
    
  // kdn Key Check --------------
  if ((digitalRead(kdn)==LOW) && (Zstop==0) && (Zpage!=TEST)) {
    if ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH)) {
      kdnHold++; ZcountSet = ZcountSet - kdnHold;
      if (kdnHold>holdindex) {
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (digitalRead(kdn)==LOW)) { ZmoveDN1(); ZcountSet=Zcount; } 
      }
    } else {
      do {} while (digitalRead(kdn)==LOW);
    } 
  } else { kdnHold = 0; }

  // kset Key Check --------------
  if (digitalRead(kset)==LOW) {
    if ((Zpage==INF) || (Zpage==TEST) || ((Zpage==RNG) && (Srng==0)) || (Zpage==CAL)) {
      if (digitalRead(Zs3sw)==Zhires) digitalWrite(Zs3sw,Zfast); else digitalWrite(Zs3sw,Zhires);
      Zupdate();
    }
    if (((Zpage==RUN) && (Srun==0))) {
      Zedm++;
      if (Zedm>2) Zedm=0;
    }
    delay(keydebounce); // debounce
  }

  // krun Key Check --------------
  if (digitalRead(krun)==LOW) {
    switch (Zpage) {
      case CAL:
        Zstop=0;
        Scal++;
        break;
      case RNG:
        Zstop=0;
        Srng++;
        break;
      case RUN:
        if (Srun > 4) Srun = 1; //resume RUN
        break; 
      case TEST:
        Zstop=0;
        break;
      case INF:
        Zstop=0;
        break;    
    }
    delay(keydebounce); // debounce
  }

  // Check A7, S3, ZScale 
  Zscalechk();
 
  // Zpage Switching (MAIN) ----------------
  switch(Zpage) {

//CAL =ok=
    case CAL:   // Z-CALIBRATION Auto Calibration 1-Inch Block using PSense 
      digitalWrite(EDM,EDMoff); //ensure EDM is OFF
      switch(Scal) {
        
        case 0: // Disp info Run to start
          Zscalechk();
          ZstatusLine();
          // "01234567890123456789"
          //mfg = "* Using "; mfg += String(Zcalblock,3); mfg += char(34); mfg += " Block"; printmfg(0,1);
          mfg = "* Clear work surface"; printmfg(0,1);
          mfg = "for Base-ZERO Detect"; printmfg(0,2);
          if (digitalRead(PSense)==LOW) { ZprobeErr(Scal); break; }
          if ((digitalRead(BSense)==LOW) || (digitalRead(ESense)==LOW)) { ZaxisErr(Scal); break; }
          progressmfg(2,Scal);  //[RUN] key to Start
          break;
        
        case 1: //seeking Zero
          progressmfg(17,Scal);  //seek DN Base Ref0        
          //seek DN Work-Zero Error if not stop by Psense
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (digitalRead(kset)==HIGH)) { ZmoveDN1(); } 
          if (digitalRead(kset)==LOW) { ZcountSet=Zcount; Scal=0; break; } //Cancel RUN
          if ((digitalRead(BSense)==LOW) || (digitalRead(ESense)==LOW)) { ZerrReload(); Scal=0; break; }      
          Zcount=0; ZcountSet=0;
          //Fine-tune zero
          ZmUP(); while ( Zcount < ZcountGap ) { ZmoveUP1(); }
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (digitalRead(kset)==HIGH))  { ZmoveDN1(); runWinch(); }
          if (digitalRead(PSense)==HIGH) { ZerrReload(); Scal=0; break; }   
          Zcount=0; ZcountSet=0;  
          dispclear(2);
          mfg = "* Base-ZERO Detected"; printmfg(0,1);
          progressmfg(10, Scal);  //goto TOP Position
          ZmUP(); while ((digitalRead(TSense)==HIGH) && (digitalRead(kset)==HIGH) && (digitalRead(krun)==HIGH) && (digitalRead(kpage)==HIGH)) { ZmoveUP1(); }
          if (digitalRead(TSense)==HIGH) { ZerrReload(); Scal=0; break; }  
          ZcountSet=Zcount;
          Scal++;
          break;  
        
        case 2: //Prep calibration
          ZstatusLine();
          Zscalechk();
            mfgtemp = String(Zscale);
            mfg="";
            for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
            mfg += (mfgtemp); mfg +=" ct/Inch  was ";  printmfg(0,1);
          mfg = "* Place "; mfg += String(Zcalblock,3); mfg += char(34); mfg += " Block"; printmfg(0,2);
          ZcountCHK = 0.5 * Zcalblock * Zscale;
          progressmfg(1,Scal);  //[S]Change [R]Next
          do {} while ((digitalRead(kset)==HIGH) && (digitalRead(krun)==HIGH) && (digitalRead(kpage)==HIGH));
          if (digitalRead(kset)==LOW) Scal=0;
          if (digitalRead(kpage)==LOW) Zpage++;
          break;
        
        case 3: //Do Calibration here
          Zupdate();
          progressmfg(18, Scal);  //seek DN 1-inch BLK
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (Zcount > ZcountCHK)) { ZmoveDN1(); }
          if ((digitalRead(PSense)==HIGH) || ( Zcount <= ZcountCHK )) { Scal=4; break; }  //B-E hit or Block not found ERR
          ZcountSet = Zcount;
          ZmUP(); while ( Zcount < ZcountSet + ZcountGap ) { ZmoveUP1(); }
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH))  { ZmoveDN1(); runWcnt(); } 
          if (digitalRead(PSense)==HIGH) { Scal=4; break; } // E-B ERR
          ZcountSet = Zcount;
          //1-inch Mark Found Update Scale and Data
          Zscale = Zcount;
          if (digitalRead(Zs3sw)==Zfast) Zscalefast=Zscale; else Zscalehires=Zscale;
          Zupdate(); ZcountSet = Zcount + ZcountProx;
          ZmUP(); while ( Zcount < ZcountSet ) { ZmoveUP1(); }
          progressmfg(19,Scal);  //Calibration DONE 
          mfgtemp = String(Zscale);
          mfg="";
          for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
          mfg += (mfgtemp); mfg +=" ct/Inch  NEW ";  printmfg(0,2);
          Scal=5;
          break;

        case 4: // ERROR
          ZcountSet = Zcount;
          ZerrReload();
          Scal=0;
          break;

        case 5: // Calibration done and exit
          anyKey(); noKey();
          progressmfg(10, Scal);  //goto TOP Position
          ZmUP(); while ((digitalRead(TSense)==HIGH) && (digitalRead(kset)==HIGH) && (digitalRead(krun)==HIGH) && (digitalRead(kpage)==HIGH)) { ZmoveUP1(); }
          ZcountSet=Zcount;
          if (digitalRead(kpage)==LOW) { Zpage++; } 
          Scal=0;
  
      }
      break;

//INF =ok=
    case INF:   //Z Info scanning Display
      ZstatusLine();
      if ((Sinf==0) || (Sinf>12)) Sinf=1;
        switch(Sinf) { 
          case 1:  Zinfor(1); Zshowr(2); Zcntr(3); break; //"r   Z-Range"
          case 2:  Zinfoz(1); Zshowz(2); Zcntz(3); break; //"z  Zero-Ref"
          case 3:  Zinfop(1); Zshowp(2); Zcntp(3); break; //"p  Park-Pos"
          case 4:  Zinfox(1); Zshowx(2); Zcntx(3); break; //"x  Prox-Pos"
          case 5:  Zinfoe(1); Zshowe(2); Zcnte(3); break; //"e   End-Pos"
          case 6:  Zinfot(1); Zshowt(2); Zcntt(3); break; //"t  wTop-Max"
          case 7:  Zinfob(1); Zshowb(2); Zcntb(3); break; //"b  wBot-Min"
          case 8:  ZinfoP(1); ZshowP(2); ZcntP(3); break; //"P wPark-Pos"
          case 9:  ZinfoX(1); ZshowX(2); ZcntX(3); break; //"X wProx-Pos"
          case 10: ZinfoG(1); ZshowG(2); ZcntG(3); break; //"G  wGap-val"
          case 11: ZinfoE(1); ZshowE(2); ZcntE(3); break; //"E  wEnd-Pos"
          case 12: ZinfoC(1); ZshowC(2); ZcntC(3); break; //"C  wCurrent"
        }
      break;

//TEST =ok=   
    case TEST:  // I/O Test Panel display, Page to Exit
      ZstatusLine(); runWspeed();
      mfg = "* Z-Axis Speed Test "; printmfg(0,1);
      mfg = "  [S] Resolution    "; printmfg(0,2);
      mfg = "[U/D] Moving Z-Axis "; printmfg(0,3);
      if (digitalRead(kup)==LOW) {
        runWfreq(); while ((digitalRead(kup)==LOW) && (digitalRead(TSense)==HIGH) && (digitalRead(TSense)==HIGH) && (digitalRead(TSense)==HIGH)) { ZmoveUP1(); } 
        ZcountSet=Zcount;
        do {} while (digitalRead(kup)==LOW);  //take only the fist short!
      }
      if (digitalRead(kdn)==LOW) {
        runWfreq(); while ((digitalRead(kdn)==LOW) && (digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH)) { ZmoveDN1(); }
        ZcountSet=Zcount;
        do {} while (digitalRead(kdn)==LOW);  //take only the fist short!
      }
      break;

//RNG =ok=
    case RNG: // Srange Z Limit Range Finder (Tested v0.30.5)
      digitalWrite(EDM,EDMoff);
      ZstatusLine();
      mfg = "TSense < ## > BSense", printmfg(0,1);
      Zcntr(2);
      switch(Srng) {
        
        case 0:
          progressmfg(2,Srng);  //[RUN] key to Start
          if (digitalRead(PSense)==LOW) { ZprobeErr(Srng); }
          break; 
        
        case 1: 
          if (digitalRead(PSense)==LOW) { ZprobeErr(Srng); Srng=0; break; }
          noKey(); 
          progressmfg(10,Srng); //goto TOP-Limit
          ZmUP(); while ((digitalRead(TSense)==HIGH) && (digitalRead(PSense)==HIGH) && (digitalRead(kset)==HIGH)) { ZmoveUP1(); }
          ZcountSet = Zcount;
          if (digitalRead(kset)==LOW) { Srng=0; break; } //Re-start
          if (digitalRead(PSense)==LOW) { ZprobeErr(Srng); anyKey(); noKey(); Srng=0; break;  }
          Zcount = 0; ZcountSet = Zcount;
          Srng++;
          break;
        
        case 2: 
          if (digitalRead(PSense)==LOW) { ZprobeErr(Srng); Srng=0; break; }
          noKey(); 
          progressmfg(11,Srng); //goto BOT-Limit
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (digitalRead(kset)==HIGH)) { ZmoveDN1(); };
          ZcountSet = Zcount;
          if (digitalRead(kset)==LOW) { Srng=0; noKey(); break; } //Re-start
          if (digitalRead(PSense)==LOW) { ZprobeErr(Srng); anyKey(); noKey(); Srng=0; break;  }
          // All set. Calculation here
          Zcount = -Zcount;
          Zmax = (float)Zcount / Zscale; //new Range inch
          Zzero = (float)Zmax / 2;  //new MID Zero inch
          Zupdate(); //update with new data
          Srng++; 
          break;
        
        case 3:
          Zcount = 0;
          ZcountSet = ZcountZero;
          progressmfg(12,Srng); //goto MID Pos.
          ZmUP(); do { ZmoveUP1(); } while (Zcount < ZcountSet);
          ZstatusLine();
          Srng++; 
          break;
        
        case 4:
          Zinfor(1); Zshowr(2);
          progressmfg(13,Srng); //Range Setup DONE
          anyKey();
          Zcount = 0; ZcountSet = 0;
          Srng = 0;
          break;
        
        case 5: // error, Reload Defalult
          ZcountSet = Zcount;
          ZerrReload();
          Srng=0;
          break;
      }      
      break;    
  
//RUN =ok=
    /* <RUN>
     * Select Job-Mode [FAST], [HiRES] ot [AUTO] (Zedm) use kset
     *    [FAST]  (Zedm) = 0 FAST thru ESense
     *    [HiRES] (Zedm) = 1 HiRES thru ESense
     *    [AUTO] (Zedm) = 2 where [FAST] > Zprox > HiRES thru Soft-Zend
     */
    case RUN:   // Z RUN
      switch(Srun) {

        case 0: //Setup Mode
          digitalWrite(EDM,EDMoff);
          //digitalWrite(Zs3sw,Zfast);  //start at Fast in ALL Mode
          if (Zedm==mHiRES) Z2hires(); else Z2fast(); //Set Speed, 
          ZstatusLine();
          if (Zedm>1) {
            mfg = "[SET]Z-Mode [U/D]End"; printmfg(0,1);
            ZlabInMM(Zend,"to ",2);
            Zstop=1;  //ensure Z lockdown
            if ((digitalRead(kup)==LOW) && (Zend<=0)) {
              keyHold++;
              if (keyHold>10) Zend += 0.01; else Zend += 0.001; 
              if (Zend>0) Zend=0;
            } else if ((digitalRead(kdn)==LOW) ) {
              keyHold++;
              if (keyHold>10) Zend -= 0.01; else Zend -= 0.001;
            } else keyHold=0;
            Zupdate();
          } else {
            Zstop=0;  
            mfg = "[SET] change Z-Mode "; printmfg(0,1);
            mfg = "End at ESense Detect"; printmfg(0,2);
          }
          // Ini AltDisp
          if ((AltDisp < 1) || (AltDisp > 3)) AltDisp=1; //Initailize Scan
          //check if PWMGen Ready
          if ((digitalRead(PWMGen)==NotReady) && (AltDisp==2)) {
            progressmfg(21,Srun);  //SysChk NotReady!
          } else {          
            progressmfg(0,Srun); //[RUN] key to Start
          }
          if ((digitalRead(krun)==LOW) ) Srun=1;
          break;

        case 1: //RUN start, Top > Work-Zero > ReCal > EDM
          ZstatusLine();
          mfg = "*JOB Work Site check"; printmfg(0,1);
          //RUN start here, Error check
          Zstop=0;
          if (digitalRead(PSense)==LOW) { ZprobeErr(Srun); break; }
          if ((digitalRead(BSense)==LOW) || (digitalRead(ESense)==LOW)) { ZaxisErr(Srun); break; }
          progressmfg(20,Srun);  //Detect Work ZERO         
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (digitalRead(kset)==HIGH)) { ZmoveDN1(); } 
          if (digitalRead(kset)==LOW) { Srun=0; noKey(); break; } //Cancel RUN
          if ((digitalRead(BSense)==LOW) || (digitalRead(ESense)==LOW))  { ZerrReload(); Srun=0; break; }      
          runWinch();
          //seek DN Work-Zero Error if not stop by Psense
          Zcount=0; ZcountSet=0;
          //Fine tune zero
          ZmUP(); while ( Zcount < ZcountGap ) { ZmoveUP1(); }
          runWinch();
          ZmDN(); while ((digitalRead(PSense)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH)) { ZmoveDN1(); runWinch(); } 
          Zcount=0; ZcountSet=0;  
          //found zero, Check Zedm mode speed
          if (Zedm==mFAST) Z2fast(); else Z2hires(); //Set to Slow in HiRES/AUTO Mode 
          ZcountSet=Zgap*Zscale;
          ZmUP(); while (( Zcount < ZcountSet) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH)) { ZmoveUP1(); } 
          runWinch();
          //all set! goto next step
          Srun++;
          break;

        case 2:
          Zstop=0;  // EDM Loop ***
          ZstatusLine();
          mfg = "** Start EDM Loop.  "; printmfg(0,1);
          progressmfg(5,Srun);  //Job in Progress...        
          while (Zcount > ZcountGap) { ZmoveDN1(); }

          //do EDM Loop until kset/kup/BSense/ESense hit
          while ((digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (digitalRead(kup)==HIGH) && (digitalRead(kset)==HIGH)) {
            //check PWMGen Ready
            if (digitalRead(PWMGen)==NotReady) {
              digitalWrite(EDM,EDMoff);
              progressmfg(21,Srun);  //SysChk NotReady!
              while (Zcount < ZcountGap) { ZmoveUP1(); }
              break;
            }
            digitalWrite(EDM,EDMon);

            switch(Zedm) {
              case mFAST:
                if ((digitalRead(PSense)==LOW)) { ZmoveUP1(); } else { ZmoveDN1(); }
                break;
              
              case mHiRES:
                if ((digitalRead(PSense)==LOW)) { ZmoveUP1();} else {
                  ZmoveDN1();
                  runWinch();
                }
                break;
              
              case mAUTO:
                while ((digitalRead(ISense)==LOW) && (digitalRead(PSense)==HIGH)) { 
                  Zclear++;
                  break; 
                }
                if (Zclear > 99) { Srun=3; break; }
                if ((digitalRead(PSense)==LOW)) { 
                  ZmoveUP1(); Zclear=0; 
                } else if ((digitalRead(PSense)==HIGH) && (digitalRead(ISense)==HIGH)) { 
                    ZmoveDN1();
                    ZshowC(1);
                }
                mfg=Zclear; printmfg(9,0); //test print
                break;
            }
          }
                  
          //Check again with EDM OFF
          digitalWrite(EDM,EDMoff);  delay (100);
          if ((digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH) && (digitalRead(kup)==HIGH) && (digitalRead(kset)==HIGH)) { // anything hit?
            //No hit, so loopback Srun=2
            Srun=2; break;
          }
          //Yes hit, check Done?
          if ((digitalRead(BSense)==LOW) || (digitalRead(ESense)==LOW)) { // Done Exit Job
            progressmfg(8,Srun); //Job End Mark Reach
            //DONE Exit to Srun=6
            Srun=6; break;
          } 
          //kup? depress Move-Up Auto Resume
          if (digitalRead(kup)==LOW) { //move up
            do { ZmoveUP1(); } while ((digitalRead(kup)==LOW) && (digitalRead(TSense)==HIGH)); // Move UP
            Srun=2; break; //ensure loop back
          }
          //kset? PAUSE, [RUN] to Resume
          if (digitalRead(kset)==LOW) { // Job Pause
            Srun=4; noKey(); break; //to Pause
          }
          // No more checks, Loop Back EDM Loop
          break;

        case 3: // Cleaning, Auto Resume 
          digitalWrite(EDM,EDMoff);
          ZstatusLine();
          progressmfg(9,Srun); //print Cleaning
          Zstop=0;
          ZmUP(); do { ZmoveUP1(); } while ((Zcount < ZcountGap) && (digitalRead(TSense)==HIGH)); // Move to Gap
          Srun=2; // Resume EDM Loop
          break;

        case 4: //Pause, Move to Gap, RUN to resume
          digitalWrite(EDM,EDMoff);
          progressmfg(6,Srun); //print Job Pause R to resume
          Zstop=0;
          //move to Gap
          if (Zcount < ZcountGap) ZmUP(); while ((Zcount < ZcountGap)) { ZmoveUP1(); } 
          ZstatusLine(); ZshowC(1);
          ZcountSet=Zcount;
          if (digitalRead(krun)==LOW) { Srun=2; noKey(); break; }  //Job Resume
          if (digitalRead(kset)==LOW) { Srun=5; noKey(); break; }  //Job Cancel
          break;    

        case 5: // Job Cancel, Exit
          digitalWrite(EDM,EDMoff);
          ZstatusLine();
          progressmfg(7,Srun); //print Job Cancel, move to Top
          Zstop=0;
          if (Zedm==mHiRES) Z2hires(); else Z2fast(); //Set Speed, 
          ZmUP(); while ((digitalRead(TSense)==HIGH) && (digitalRead(kset)==HIGH)) { ZmoveUP1(); }  // Move to Top
          ZstatusLine(); ZshowC(1);
          ZcountSet=Zcount;
          anyKey(); noKey();
          Srun=0;
          break;

        case 6: // Job Finish
          digitalWrite(EDM,EDMoff);
          ZstatusLine();
          progressmfg(3,Srun); //JOB Finished.  move to Top
          Zstop=0;
          if (Zedm==mHiRES) Z2hires(); else Z2fast(); //Set Speed, 
          ZmUP(); while ((digitalRead(TSense)==HIGH)) { ZmoveUP1(); }  // Move to Top
          ZstatusLine(); ZshowC(1);
          ZcountSet=Zcount;
          anyKey(); noKey();
          Srun=0;
          break;

      } //Srun
      break;  //RUN
  
  } //Zpage switch END here 

  // Auto Update Timer
  msNow = millis();
  if (msNow-msLast > msWait) {
    msLast = msNow;
    if ((Sinf>0) && (digitalRead(krun)==HIGH)) Sinf++;  //pause INF display
    if (AltDisp>0) AltDisp++;  //Alternate display Flag
  }

}
/// Program End Here
/* ------------------------------------------------------------------------*/
/*  F U N C T I O N   S U B   R O U T I N E S
/* Condition Check --------------------------------------------------------*/
/// Check AnyKey Press
void anyKey() {
  do {} while ((digitalRead(kpage)==HIGH) && (digitalRead(krun)==HIGH) && (digitalRead(kset)==HIGH) && (digitalRead(kup)==HIGH) && (digitalRead(kdn)==HIGH) && (digitalRead(kset)==HIGH));
}

/// Clear any Key Press
void noKey() {
  do {} while ((digitalRead(kpage)==LOW) || (digitalRead(krun)==LOW) || (digitalRead(kset)==LOW) || (digitalRead(kup)==LOW) || (digitalRead(kdn)==LOW) || (digitalRead(kset)==LOW));
}

/// Z Default Reload Works Setup Default
void Zdefault() {
  Zmax = ZmaxDefault;
  Zzero = Zmax /2;  //Default MID Pos as Work Zero
  Zupdate();
}

/// Z Scale Update and Check
void Zupdate() {
  Zscalechk();  //FAST or HiRES get Zscale
  ZcountZero = (float)Zzero * Zscale; //machine offset counts
  ZcountMMax = ((float)Zmax * Zscale); //machine Z range counts
  ZcountMax = ZcountMMax - ZcountZero; //work Top-Limit
  ZcountMin = -ZcountZero;  //work Bot-Limit
  ZcountProx = ((float)Zprox * Zscale); //Delta from wZero
  ZcountEnd = ((float)Zend * Zscale); //Delta from wZero
  ZcountGap = ((float)Zgap * Zscale); //Delta from wZero
}

/// Z Parameter Check  Max > Park > Prox > Gap > Zero > End > Min
/*
void ZcountCheck() {
  if ((Zscale < ZscaleMin) || (Zscale > ZscaleMax)) ZerrStop(); //chk against #defind
  if ((ZcountPark < ZcountProx) || (ZcountProx < ZcountGap) || (ZcountGap < 0) || (0 < ZcountEnd) || (ZcountEnd < ZcountMin)) ZerrStop();
  if ((ZcountMax < (ZcountZero + ZcountPark)) || (ZcountZero < 0)) ZerrStop();
}
*/
/// Zscale Check
void Zscalechk() {
  Ain7 = analogRead(A7pulse)/2; //Read once F<0 - 512>S
  Ain7 += pulseMin; //Min PulseWidth at 0
  if (digitalRead(Zs3sw)==Zhires) { 
    Zscale = Zscalehires;
    Ain7 = Ain7/2;
  } else { 
    Zscale = Zscalefast;
  }
}

/// Z Speed Setup to FAST
void Z2fast() {
  digitalWrite(Zs3sw,Zfast);
  Zupdate();
}

/// Z Speed Setup to SLOW
void Z2hires() {
  digitalWrite(Zs3sw,Zhires);
  Zupdate();
}

/* Z-ERROR >>------------------------------------------------------------------------*/
/*
/// Z Error Stop
void ZerrStop() {
  Zstop=1;
  ZerrReload();
}
*/
/// Z-Drive ERR Reload Default
void ZerrReload() {
  lcd2.clear();
  mfg = "ERROR"; printmfg(7,1);
  mfg = "Reload Default"; printmfg(3,2);
  Zdefault();
  ZcountSet = Zcount;
  delay (disptime);
}

/// Z-Drive Probe Error print
void ZprobeErr(int j) {
  mfg = ("! Probe ERROR "); mfg += (j); mfg += "   ";
  printmfg(2,3);
}

/// Z-Axis Error print
void ZaxisErr(int j) {
  mfg = ("! Zaxis ERROR "); mfg += (j); mfg += "   ";
  printmfg(2,3);
}

/* Z-MOVEMENT >>------------------------------------------------------------------------*/
/// Z move to Prox Absolute POS
void Zmove2prox() {
  ZcountSet = ZcountProx;
  do { Z2countSet(); } while (Zcount != ZcountSet );
}

/// Z move to Park
void Zmove2park() {
  ZcountSet = ZcountPark;
  do { Z2countSet(); } while (Zcount != ZcountSet );
}

/// Z move to GAP
void Zmove2gap() {
  ZcountSet = ZcountGap;
  do { Z2countSet(); } while (Zcount != ZcountSet );
}
 
/// Z move to Set Position
void Z2countSet() {
  if ((Zstop!=0) || (Zcount == ZcountSet)) return;
  if (Zcount < ZcountSet) { 
    do { ZmoveUP1(); } while ((Zcount < ZcountSet) && (digitalRead(PSense)==HIGH) && (digitalRead(kset)==HIGH) && (digitalRead(TSense)==HIGH));
    if (digitalRead(PSense)==LOW || digitalRead(TSense)==LOW) ZcountSet = Zcount;
  } else {
    do { ZmoveDN1(); } while ((Zcount > ZcountSet) && (digitalRead(PSense)==HIGH) && (digitalRead(kset)==HIGH) && (digitalRead(BSense)==HIGH) && (digitalRead(ESense)==HIGH));
    if (digitalRead(PSense)==LOW || digitalRead(BSense)==LOW) ZcountSet=Zcount;
  }
}

/// Z Move UP 1
void ZmoveUP1() {
  if (Zstop!=0) return;
  digitalWrite(Zdir, ZdirUP);
  digitalWrite(Zclk, ZclkH); delayMicroseconds(Ain7); digitalWrite(Zclk, ZclkL); delayMicroseconds(Ain7);
  //digitalWrite(Zclk, ZclkH); delayMicroseconds(Ain7-30); digitalWrite(Zclk, ZclkL); delayMicroseconds(Ain7-5);
  Zcount++;  
}

/// Z Move DN 1
void ZmoveDN1() {
  if (Zstop!=0) return;
  digitalWrite(Zdir, ZdirDN);
  digitalWrite(Zclk, ZclkH); delayMicroseconds(Ain7); digitalWrite(Zclk, ZclkL); delayMicroseconds(Ain7);
  //digitalWrite(Zclk, ZclkH); delayMicroseconds(Ain7-30); digitalWrite(Zclk, ZclkL); delayMicroseconds(Ain7-5);
  Zcount--;  
}

/// Zmode mUP
void ZmUP() {
    Zmode=mUP; lcd2.setCursor(6,0); lcd2.print(Zmodemsg[Zmode]);
}

/// Zmode mDN
void ZmDN() {
    Zmode=mDN; lcd2.setCursor(6,0); lcd2.print(Zmodemsg[Zmode]);
}

/* DISPLAY >>------------------------------------------------------------------------*/
/// Print mfg on Cursor i, Line j
void printmfg(int i, int j) {
  lcd2.setCursor(i,j); lcd2.print(mfg);
}

/// Clear line J
void dispclear(int j) {
  lcd2.setCursor(0,j); lcd2.print ("                    "); 
}

/// Status w/ ZCLK Freq 8-chr
void runWfreq() {
  mfg = "";
  //float ical = 1000000 / (Ain7 * 2); //freq at On+Off
  float ical = 1000000 / ((Ain7 + A7comp) * 2); //freq at On+Off+Comp
  mfgtemp = String(ical,0); 
  for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp; mfg += "Hz";
  lcd2.setCursor(6,0); lcd2.print(mfg);
}

/// Status w/ Inch/S Speed 8-chr
void runWspeed() {
  mfg = "";
  //float ical = 1000000 / (Ain7 * 2); //freq at On+Off 
  float ical = 1000000 / ((Ain7 + A7comp) * 2); //freq at On+Off+Comp
  ical = ical / Zscale;
  mfgtemp = String(ical,3); 
  for (int j=5; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp; mfg += char(34); mfg += "/s";
  lcd2.setCursor(6,0); lcd2.print(mfg);
}

/// Status w/ Current Counts as Zmodemsg 8-chr
void runWcnt() {
  mfg = "";
  mfgtemp = String(Zcount);
  for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp; mfg += "ct";
  lcd2.setCursor(6,0); lcd2.print(mfg);
}

/// Status w/ Current Counts as Zmodemsg 8-chr
void runWmm() {
  mfg = "";
  Zdata = (float)Zcount / Zscale * 25.4;
  mfgtemp = String(Zdata,2);
  for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp; mfg += "mm";
  lcd2.setCursor(6,0); lcd2.print(mfg);
}

/// Status w/ current Inch 8-chr
void runWinch() {
  mfg = "";
  Zdata = (float)Zcount / Zscale;
  mfgtemp = String(Zdata,3);
  for (int j=7; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp; mfg += char(34);
  lcd2.setCursor(6,0); lcd2.print(mfg);
}

/// Z Status Display in Line0 (6)+(8)+(6)
void ZstatusLine() {
  mfg = (Zpagemsg[Zpage]); //6-chr
  if (Srun==2) {
    mfg += Zmodemsg[5];  //<< >> 8-chr
  } else {
    if (Zstop==0) mfg += Zmodemsg[0]; else { mfg += Zmodemsg[mPause]; } //Pause at Zstop=1
  }
  if (Zpage==RUN) {
    mfg += (Zedmmsg[Zedm]);
  } else {
    if (digitalRead(Zs3sw)==Zhires) mfg += Zedmmsg[mHiRES]; else mfg += Zedmmsg[mFAST];; //6-chr
  }
  printmfg(0,0);
}

/// Z Info Display in Line(j) Lowwer-case =Mechine, Upper-case = from Work Zero
void Zinfor(byte j) { lcd2.setCursor(0,j); showInfo((Zmax*Zscale),          "r   Z-Range"); }
void Zinfot(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMax),            "t  wTop-Max"); }
void Zinfob(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMin),            "b  wBot-Min"); }
void Zinfoz(byte j) { lcd2.setCursor(0,j); showInfo((ZcountZero),           "z  Zero-Ref"); }
void Zinfop(byte j) { lcd2.setCursor(0,j); showInfo((ZcountPark+ZcountZero),"p  Park-Pos"); }
void ZinfoP(byte j) { lcd2.setCursor(0,j); showInfo((ZcountPark),           "P wPark-Pos"); }
void Zinfox(byte j) { lcd2.setCursor(0,j); showInfo((ZcountProx+ZcountZero),"x  Prox-Pos"); }
void ZinfoX(byte j) { lcd2.setCursor(0,j); showInfo((ZcountProx),           "X wProx-Pos"); }
void ZinfoC(byte j) { lcd2.setCursor(0,j); showInfo((Zcount),               "C  wCurrent"); }
void ZinfoT(byte j) { lcd2.setCursor(0,j); showInfo((ZcountSet),            "T   wTarget"); }
void Zinfoe(byte j) { lcd2.setCursor(0,j); showInfo((ZcountEnd+ZcountZero), "e   End-Pos"); }
void ZinfoE(byte j) { lcd2.setCursor(0,j); showInfo((ZcountEnd),            "E  wEnd-Pos"); }
void ZinfoG(byte j) { lcd2.setCursor(0,j); showInfo((ZcountGap),            "G  wGap-val"); }

void Zshowr(byte j) { lcd2.setCursor(0,j); showInMM((Zmax*Zscale), "r"); }
void Zshowt(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMax), "t"); }
void Zshowb(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMin), "b"); }
void Zshowz(byte j) { lcd2.setCursor(0,j); showInMM((ZcountZero),"z"); }
void Zshowp(byte j) { lcd2.setCursor(0,j); showInMM((ZcountPark+ZcountZero),"p"); }
void ZshowP(byte j) { lcd2.setCursor(0,j); showInMM((ZcountPark),"P"); }
void Zshowx(byte j) { lcd2.setCursor(0,j); showInMM((ZcountProx+ZcountZero),"x"); }
void ZshowX(byte j) { lcd2.setCursor(0,j); showInMM((ZcountProx),"X"); }
void ZshowC(byte j) { lcd2.setCursor(0,j); showInMM((Zcount),    "C"); }
void ZshowT(byte j) { lcd2.setCursor(0,j); showInMM((ZcountSet), "T"); }
void Zshowe(byte j) { lcd2.setCursor(0,j); showInMM((ZcountEnd+ZcountZero), "e"); }
void ZshowE(byte j) { lcd2.setCursor(0,j); showInMM((ZcountEnd), "E"); }
void ZshowG(byte j) { lcd2.setCursor(0,j); showInMM((ZcountGap), "G"); }

void Zcntr(byte j) { lcd2.setCursor(0,j); showInCNT((Zmax*Zscale), "r"); }
void Zcntt(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMax), "t"); }
void Zcntb(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMin), "b"); }
void Zcntz(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountZero),"z"); }
void Zcntp(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountPark+ZcountZero),"p"); }
void ZcntP(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountPark),"P"); }
void Zcntx(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountProx+ZcountZero),"x"); }
void ZcntX(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountProx),"X"); }
void ZcntC(byte j) { lcd2.setCursor(0,j); showInCNT((Zcount),    "C"); }
void ZcntT(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountSet), "T"); }
void Zcnte(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountEnd+ZcountZero), "e"); }
void ZcntE(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountEnd), "E"); }
void ZcntG(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountGap), "G"); }

/// Display Progress msg on Line#3 i=index, j=Step
void progressmfg(int i, int j) {
  mfg = (j); mfg += " "; //2-chr
  strcpy_P(buffer, (char*)pgm_read_word(&(Zprogress[i]))); 
  mfg += (buffer);
  printmfg(0,3);
}

/// Display ZlabInMM(Inch,Label,Line#) in [NNN -x.xxx" -xx.xxmm]  //20-chr
void ZlabInMM(float Zdata, String m, int j) {
  mfg = m + " ";
  mfgtemp = String(Zdata,3);
  for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp + char(34) + " ";
  mfgtemp = String(Zdata*25.4,2);
  for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp + "mm";
  lcd2.setCursor(0,j); lcd2.print(mfg);
}

/// Display Inch + Count "+xx.xxx" T +xxxxxcnt" from Count
void showInCNT(float Zdata, String m) {
  mfgtemp = String(Zdata/Zscale,3);
  mfg = "";
  for (int j=7; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp + char(34) + " " + m + " ";
  mfgtemp = String(Zdata,0);
  for (int j=7; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp + "ct";
  lcd2.print (mfg);  
}

/// Display Inch + MM "+xx.xxx" T +xxx.xxmm" from Count
void showInMM(float Zdata, String m) {
  mfgtemp = String(Zdata/Zscale,3);
  mfg = "";
  for (int j=7; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp + char(34) + " " + m + " ";
  mfgtemp = String(Zdata/Zscale*25.4,2);
  for (int j=7; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp + "mm";
  lcd2.print (mfg);  
}

/// Display Inch + Info Text (11-chr) from Count
void showInfo(float Zdata, String m) {
  mfgtemp = String(Zdata/Zscale,3);
  mfg = "";
  for (int j=7; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp + char(34) + " " + m;
  lcd2.print (mfg);  
}

/// RESET BOOT -----------------------------------------------------------------------
void resetBoot() {
  asm volatile ("  jmp 0");
}

/// F/W End Here
/* ------------------------------------------------------------------------*/