#define info0  "F/W: Z0325-C01_v0.21" 
#define info1  "Date:     04/05/2019"
#define info2  "Assy:    CES180325-3"
#define info3  "EMC4 Build-01   ZDRV"
/* 
 * Z0325-C01_v0.21 ----------------------------------------
 *  Arduino 1.8.8 Program 85%, Memory 67%
 *  Test with 150mm ZDRV4 T2 5.927" 12V Motor
 *    Default in HiRES (Mode.16) mode
 *  LEVEL POT --------------------------------
 *    PWM Pulse Current Adj 0.1A ~ 15A
 *  OVR-ON SW (PWM Output Controls) ----------
 *    MANUAL = Override Always ON
 *    AUTO = Firmeware Controls ON-OFF
 *  ====================================================================  
 *  Z-Axis Label Remark
 *    ZcountMMax -------- TSense (Top Limit SW)
 *    ZcountMax --------- (SoftMAX = TSense-ZcountProx)
 *    ZcountProx -------- (Process standby position)
 *    ZcountZero -------- (Work Zero Reference)
 *    ZcountEnd --------- (zEND EDM ending target)
 *    ZcountMin --------- (SoftMIN = BSense+ZcountProx)
 *    ZcountMMin -------- BSense (Bottom Limit SW)
 *  ====================================================================  
 *  Power-up Initializing Sequence
 *    1. Display F/W Version
 *    2. Load Z Setup default data
 *    3. Goto <PORT> Checking Page
 *        'Z-UP' key to SoftMAX (Press+Hold override to Top Limit)
 *        'Z-DN' key to SoftMIM (Press+Hold override to Bottom Limit)
 *        'PAGE' key to Exit
 *    4. Check KEY while Initialize Z
 *        Z.UP to TSense (Top Limit)
 *        Z.DN to Z-Max (Top Park)
 *        Reboot if 'PAGE' key hit
 *        Press+Hold 'PAGE' key while Reboot to bypass initialization
 *  ====================================================================  
 *  PAGE SW (UP or DN) -----------------------
 *    Press + Hold jump to <RUN>
 *    <RUN>
 *      'SET' key to change Run-Mode;
 *        [FAST] Mode.8, High Speed Rough-cut w/ 0.0000983" per Step Z Resolution
 *        [HiRES] Mode.16, Default w/ 0.0000492" per Step Z Resolution
 *        [zEND] Mode.16 HiRES w/ Defined Depth
 *      'RUN' key to Start/Resume EDM Operation
 *      'Z-UP' key to Pause EDM Operation
 *      'Z-DN' key to Ending EDM Operation
 *    <INFO>
 *      Parameters Display in scanning mode
 *      'RUN' key to Pause
 *      'SET' key to Retract
 *    <RANGE>
 *      Default in HiRES Mode.16
 *      Removed all Probe
 *      Step counts between TSense & BSense
 *      'RUN' key to Start
 *    <CAL>
 *      Default in HiRES Mode.16
 *      Using Fine point Probe and 1"(inch) Test Block
 *      Clear & explode the Base Surface (Ref 0)
 *      'RUN' key to Start Base Zero Detection
 *      Move to Park position as Zero detected
 *      Place 1" Test Block between Base Surface and Probe
 *      'RUN' key to measure 1" Test Block in Step Counts
 *      'SET' to Save, 'RUN' to start over, 'PAGE' to exit
 *    <TEST>
 *      System Test Page
 *      'Z-UP', 'Z-DN' Moving Z Axis
 *      'Z-SPEED POT' Z Axis Speed Adjust
 *      'SET' Changing Z.Mode [FAST]/[HiRES]
 *      'PAGE' key to Exit
 *    <VER>
 *      Display Firmware Info
 *  Z-UP/DN SW ----------------------------
 *    'Z-UP' Move UP till Soft MAX Possition, Press+Hold to TSense Limit
 *    'Z-DN' Move DN till Soft MIM Possition, Press+Hold to BSense Limit
 *  SET/RUN SW ----------------------------
 *    Processing keys
 *  ====================================================================  
 *  CURRENT SENSE POT --------------------
 *    EDM Current Sense Adjust
 *  Z-SPEED POT
 *    Z-Axis Speed Controls
 *  MOTOR VOLTAGE SW ---------------------
 *    [+24V] [OFF] [+12V]
 *  RESET SW -----------------------------
 *    Hardware Reset for both CPU
 * ====================================================================  
 * LCD 2004 I2C (5V)
 *    A5 = SCL
 *    A4 = SDA
 * --------------------------------------------------------
 *    Pro MiniZDRV Assignments
 *      A7  Z.Speed Zclk Setting
 *      A6  not used
 *      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* (Default to ON)
 *       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 EMC4-01, EMC4-02,
  //LiquidCrystal_I2C lcd2(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // 2004A LCD EMP4-01, EMP4-02, 
  
// 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 LOW
    #define ZdirDN HIGH
  #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 (Emergency) 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 Default Value (OK)
  #define Z60mmM1scale  1269 (0.0007877)
  #define Z60mmM2scale  2539 (0.0003938)
  #define Z60mmM8scale  10182 (0.0000985)
  #define Z60mmM16scale  20312 (0.0000492)
*/

// Z-Drive Default Value 
  //Mode.8 3on 4on   = fine  = 10156 (0.0000985) as FAST
  //Mode.16 3off 4on = Ultra = 20312 (0.0000492) as HiRES
  long ZfastDefault;  // HiRES * 0.5 Default steps counts per Inch (TB6560 Mode8 S3=ON S4=ON) 60mm FAST 
  long ZhiresDefault = 20312;  //Default steps counts per Inch (TB6560 Mode16 S3=OFF S4=ON) 60mm HiRES 
  //float ZmaxDefault = 2.158; //Z Top 2.1 Inch (60mm Axis)
  float ZmaxDefault = 5.927; //Z Top 5.927 Inch (150mm Axis)

// Z-Drive Operating (OK)
  float ZmaxRNG = 0; //Z Range from RNG Finder
  long ZfastCAL = 0;  //FAST steps counts per Inch From CAL
  long ZhiresCAL = 0;  //HiRES steps counts per Inch From CAL
  #define ZscaleRange 10 //-% err counts
  #define Zcalblock 1.0 //CAL Block DIA or Thickness in Inch
  float Zprox = 0.05; //Prox value in Inch
  float Zgap = 0.03; //Gap value in Inch
  float Zend = -0.1; //zEnd Finish Depth in Inch
  float Zzero = 1;  //Zero value in Inch
  float Zcurrent = 1;  //Current value in Inch
  long Zscale;  //step counts per Inch
  long ZcountRange;  //Range counts
  long ZcountGap;  //Gap in Counts (VAL)
  long Zcount; //Current Step Count (Work)
  long ZcountSet; //Target Step Count (Work)
  long ZcountSave; //+UP -DN Temp Display Counts (Work)
  long ZcountCHK; // Z Temp storage for Error Checking

// Z-Drive Position Counts from wZero per Scale, Range
  long ZcountMMax; //Z Machine Top Limit TSense
  long ZcountMax; //Top Soft Limit Counts (B)
  long ZcountProx;  //Prox in Counts (Above Zero)
  long ZcountZero; //Z Machine Work Zero offset (Ref)
  long ZcountEnd; //Define Finish Depth (from Work Zero)
  long ZcountFin; //Actual Finish Depth (from Work Zero)
  long ZcountMin; //Bottom Soft Limit Counts ()
  long ZcountMMin; //Z Machine Bot Limit BSense (BOT = Machine-Zero)

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

// 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
  #define ZclearIndex 75  //Zclear Index

// 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 Cancel/DONE!  ";   
  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! go TOP";   
  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 = "[RUN]Go [else]Exit"; 
  const char string_22[] PROGMEM = "[S]Cancel [R]Next ";   
  const char string_23[] PROGMEM = "SysCheck NotReady!";  
  const char string_24[] PROGMEM = "goto Park Position";  

// 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, string_23, string_24 };
  char buffer[24];    // make sure this is large enough for the largest string it must hold

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

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

// 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 350 //delay for keydebounce
  #define holdindex 200  //Key refresh count before switch to high speed 
  byte pagemsg = 0; //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 = 2000; //default 1-second Wait
  float Zdata;  //common shares data
  double Aread; //common read
  int kupHold; //UP long press
  int kdnHold; //DN long press
  int kpageHold; //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);
  
  //LCD setup
  lcd2.begin(20, 4);

  // Read A7 Zclk Setting
  getZin7();
  
  // Enable Z-Drive
  digitalWrite(Zen,Zon);

  // Display Info
  showFW();
  delay(disptime);
  // Load Z Setup Default
  Zdefault();
  
  // Bypass initialize if kpage pressed
  if (digitalRead(kpage)==HIGH) {
    lcd2.clear(); 
    lcd2.setCursor(2,1);lcd2.print("Initializing...");
    lcd2.setCursor(0,3);lcd2.print("Hold PAGE to bypass!");
    while (digitalRead(TSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH && digitalRead(krun)==HIGH && digitalRead(kset)==HIGH) { ZmoveUP1(); } 
      delay(250);
      lcd2.clear();
      if (digitalRead(TSense)==HIGH) { // Not Ready
        lcd2.setCursor(2,1);lcd2.print("System NOT Ready!");
        lcd2.setCursor(3,3); lcd2.print("R E B O O T !!!");
        delay(disptime);
        resetBoot();
    } else { // Ready 
        Zcount = ZcountMMax;
        Zmove2max();
        lcd2.clear();
        lcd2.setCursor(3,1);lcd2.print("System Ready!");
        delay(disptime);
    }
  } else {
    lcd2.clear();
    lcd2.setCursor(1,1); lcd2.print("Bypass Initialize!");
    do {} while (digitalRead(kpage)==LOW);
  }
  

  // StartUp Page
  if (Zcount!=ZcountMax) {
    Zcount = 0; ZcountSet = Zcount; 
    Zpage=INF; 
  } else {
    Zpage = RUN;
  }
  pageTitle();

}
/// 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++; kpageHold++;
    if (kpageHold>3) Zpage = RUN;
    if (Zpage>lastpage) Zpage = firstpage;
    if (Zpage==RUN) Zedm = ZedmSave;
    if (Zpage==RNG || Zpage==CAL) {
      Zedm = mHiRES; Zreload();
    }
    delay(keydebounce); // debounce
  } else kpageHold = 0;

  // kup Key Check --------------
  if (digitalRead(kup)==LOW && Zstop==0) {
    if (Zpage==TEST) PortTest();
    kupHold = 0;
    ZmUP(); while (digitalRead(TSense)==HIGH && digitalRead(kup)==LOW && ZcountMax>Zcount) { ZmoveUP1(); }
    while (digitalRead(kup)==LOW && digitalRead(TSense)==HIGH) { 
      delay(100); kupHold++;
      while (digitalRead(kup)==LOW && digitalRead(TSense)==HIGH && kupHold>20) ZmoveUP1(); 
    }
    ZcountSet=Zcount;
    if (Zpage==TEST) PortTest();
  } 
    
  // kdn Key Check --------------
  if (digitalRead(kdn)==LOW && Zstop==0) {
    if (Zpage==TEST) PortTest();
    kdnHold = 0;
    ZmDN(); while (digitalRead(kdn)==LOW && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(PSense)==HIGH && Zcount>ZcountMin) { ZmoveDN1(); } 
    while (digitalRead(kdn)==LOW && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(PSense)==HIGH) { 
      delay(100); kdnHold++;
      while (digitalRead(kdn)==LOW && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(PSense)==HIGH && kdnHold>20) ZmoveDN1(); 
    }
    ZcountSet=Zcount;
    if (Zpage==TEST) PortTest();
  }   

  // kset Key Check --------------
  if (digitalRead(kset)==LOW) {
    switch (Zpage) {
      case RUN:
        if (Srun==0) {
          Zedm++; 
          if (Zedm>mZEND) Zedm=mFAST;
          ZedmSave = Zedm;
          Zreload();
        }
        break;
      case TEST:
      case INF:
        PortTest();
        if (Zedm==mFAST) {
          Zedm = mHiRES;
        } else {
          Zedm = mFAST;
        }
        Sinf = 0;
        Zreload();
        break;
      case RNG:
        Zedm = mHiRES;
        Zreload();
        break;
      case CAL:
        Zedm = mHiRES;
        Zreload();
        break;
    }
    //delay(keydebounce); // debounce
    do {} while (digitalRead(kset)==LOW);
  }

  // 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;
        PortTest();
        break;
      case INF:
        Zstop=0;
        break;    
    }
    //delay(keydebounce); // debounce
    do {} while (digitalRead(krun)==LOW);
  }
  
  // Read A7 Zclk
  getZin7();
 
  // Zpage Switching (MAIN) ----------------
  switch(Zpage) {

//RUN OK 3/17
    /* <RUN>
     * Select Job-Mode Zedm=0[FAST], 1[HiRES] ot 2[zEND] by kset
     */
    case RUN:   // Z RUN
      switch(Srun) {

        case 0: //Setup Mode
          digitalWrite(EDM,EDMoff);
          pageTitle(); runWinch();
          if (Zedm>1) {
            mfg = "[S]Mode [U/D]Pau/End"; printmfg(0,1);
            ZlabInMM(Zend,"at ",2);
            Zstop=1;  //ensure Z lockdown
            if (digitalRead(kup)==LOW && Zend<0) {
              Zend += 0.001; kupHold++;
              if (kupHold>10) Zend += 0.01; 
            } else kupHold=0;
            if (digitalRead(kdn)==LOW && Zend>(ZcountMin/Zscale)) {
              Zend -= 0.001; kdnHold++;
              if (kdnHold>10) Zend -= 0.01;
            } else kdnHold=0;
            if (Zend>0) Zend=0;
            if (Zend<(ZcountMin/Zscale)) Zend=(ZcountMin/Zscale);
            Zupdate();
          } else {  //Zedm=0[FAST] or 1[HiRES]
            Zstop=0;  
            mfg = "[SET] change Z-Mode "; printmfg(0,1);
            if (Zedm<mZEND) mfg = "[UP]=Pause  [DN]=End";
            printmfg(0,2);
            if (digitalRead(kup)==LOW) {
              ZmUP(); while (digitalRead(kup)==LOW && digitalRead(TSense)==HIGH && (Zcount<ZcountMax)) { ZmoveUP1(); } 
              ZcountSet = Zcount;
            }
            if (digitalRead(kdn)==LOW) {
              ZmDN(); while (digitalRead(kdn)==LOW && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(PSense)==HIGH && (Zcount>ZcountMin)) { ZmoveDN1(); } 
              ZcountSet = Zcount;
           }
          }
          progressmfg(0,Srun); //[RUN] key to Start
          if (digitalRead(krun)==LOW) Srun=1;
          break;

        case 1: //RUN start, Top > Work-Zero 
          pageTitle(); runWinch();
          mfg = "*JOB Work Zero check"; printmfg(0,1);
          //RUN start here, Error check
          Zstop=0;
          //Clear P/B/ESenses
          ZmUP(); while (digitalRead(PSense)==LOW || digitalRead(BSense)==LOW || digitalRead(ESense)==LOW) {
            ZprobeErr(Srun);
            Zmove2max();
            anyKey();
          }
          progressmfg(20,Srun);  //Detect Work ZERO         
          ZmDN(); while (digitalRead(PSense)==HIGH && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && Zcount>ZcountMin && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveDN1(); } 
          if (digitalRead(PSense)==HIGH) { ZprocessErr(Srun); Srun=0; break; }  //error start over
          while (digitalRead(kup)==LOW && digitalRead(TSense)==HIGH) { ZmUP(); ZmoveUP1(); }
            //Get Zero Ref Counts
            ZcountMMax = ZcountMax + ZcountGap - Zcount;
            ZcountZero = ZcountRange - ZcountMMax;
            Zzero = (float)ZcountZero / Zscale;
            Zupdate();
            Zcount=0;  
          //Fine-tune zero
          ZcountSet = ZcountGap;
          ZmUP(); while ( Zcount < ZcountSet) { ZmoveUP1(); }
          while (digitalRead(PSense)==HIGH && Zcount>ZcountMin && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveDN1(); runWinch(); } 
          if (digitalRead(PSense)==HIGH) { ZprocessErr(Srun); Scal=0; break; } 
            //Get Zero Ref Counts
            ZcountMMax = ZcountMax + ZcountGap - Zcount;
            ZcountZero = ZcountRange - ZcountMMax;
            Zzero = (float)ZcountZero / Zscale;
            Zupdate();
            Zcount=0;  
          ZcountSet = Zcount; 
          //found zero, prep for next
          dispclear(2); mfg = "* Base-ZERO Detected"; printmfg(0,1);
          runWinch();      
          //found zero, Check Zedm mode
          ZmUP(); while (Zcount<ZcountProx) { ZmoveUP1();}
          runWinch();
          //all set! goto next step
          mfg = "** EDM Loop Ready..."; printmfg(0,2);
          progressmfg(21,Srun);  //[RUN]Go [else]Exit
          anyKey();
          if (digitalRead(krun)==LOW) { Srun++; } else { Srun=5; }
          break; 

        case 2:
          Zstop=0;  // EDM Loop ***
          if (Zedm==mZEND) { lcd2.setCursor(6,0); lcd2.print(Zmodemsg[5]); } else runWinch();          
          mfg = "** Start EDM Loop.  "; printmfg(0,1);
          progressmfg(5,Srun);  //Job in Progress...        
          Zclear=0; //clear cleaning index
          Zmodedisp(2); // Z-Mode MSG on Line#2
          while (Zcount>ZcountGap) { ZmoveDN1(); } 

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

            // Cleaning Check
            if (digitalRead(ISense)==LOW) { Zclear++; }
            while ((digitalRead(PSense)==LOW)) { ZmoveUP1(); }

            switch(Zedm) {
              case mFAST:
              case mHiRES:
              while (digitalRead(PSense)==HIGH && digitalRead(ISense)==HIGH && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH) { ZmoveDN1(); }
              break;
              
              case mZEND:
              while (Zcount>ZcountEnd && digitalRead(PSense)==HIGH && digitalRead(ISense)==HIGH && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH) { ZmoveDN1(); }
              ZshowC(1); 
              break;
            }
            
          } //end do EDM Loop
                  
          //Check again with EDM OFF
          digitalWrite(EDM,EDMoff);  delay (250);

          if (Zclear>ZclearIndex) { Zclear=0; Srun=3; break; }
          
          if ((Zedm==mZEND && Zcount>ZcountEnd) && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kup)==HIGH && digitalRead(kdn)==HIGH) { // Nothing hit
            //No hit, so loopback Srun=2
            Srun=2; break;
          }
          
          //Yes something hit, check Status ------------
          //kup? Pause
          if (digitalRead(kup)==LOW) { 
            do {} while (digitalRead(kup)==LOW);
            Srun=4; break; //Pause
          }
          //Done?
          if (digitalRead(BSense)==LOW || digitalRead(ESense)==LOW || (Zedm==mZEND && Zcount<=ZcountEnd)) { // Done Exit Job
            progressmfg(8,Srun); //Job End Mark Reach
            //DONE Exit to Srun=6
            Srun=6; break;
          } 
          //kdn? Cancel
          if (digitalRead(kdn)==LOW) { // Job Pause
            do {} while (digitalRead(kdn)==LOW);
            Srun=6; break; //to Pause
          }
          // No more status, Loop Back EDM Loop
          break;

        case 3: // Cleaning, Auto Resume 
          digitalWrite(EDM,EDMoff);
          progressmfg(9,Srun); //print Cleaning...
          Zstop=0;
          ZmUP(); while (Zcount<ZcountGap) { ZmoveUP1(); }; // 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 Prox
          while ((Zcount < ZcountProx)) { ZmoveUP1(); } 
          runWinch(); ZshowC(1);
          ZcountSet=Zcount;
          if (digitalRead(krun)==LOW) { 
            do {} while (digitalRead(krun)==LOW);
            Srun=2; break; 
          }  //Job Resume
          break;    

        case 5: // Job Cancel, Exit
          digitalWrite(EDM,EDMoff);
          dispclear(1);dispclear(2);
          runWinch();
          progressmfg(7,Srun); //print Job Cancel, go Top
          Zstop=0;
          ZmUP(); while (digitalRead(TSense)==HIGH && Zcount<ZcountMax) { ZmoveUP1(); }  // Move to Top
          ZcountSet=Zcount;
          Srun=0;
          break;

        case 6: // Job Finish
          digitalWrite(EDM,EDMoff);
          ZcountFin = Zcount; // Get Finish Counts
          ZinfoF(1); ZshowF(2); // Finish
          progressmfg(3,Srun); //JOB Finished move to Top
          Zstop=0;
          ZmUP(); while (digitalRead(TSense)==HIGH && Zcount<ZcountMax) { ZmoveUP1(); }  // Move to Top
          runWinch(); 
          ZcountSet=Zcount;
          anyKey();
          Srun=0;
          break;

      } // end Srun
      break;  //RUN

///RNG =OK=
    case RNG: // Srange Z Limit Range Finder
      digitalWrite(EDM,EDMoff);
      pageTitle(); runWinch();
      mfg = "TSense < ## > BSense", printmfg(0,1);
      Zcntr(2);
      
      switch(Srng) {
        case 0:
          progressmfg(0,Srng);  //[RUN] key to Start
          break; 
        
        case 1: //Processing Start, Move to TSense TOP Limit
          if (digitalRead(PSense)==LOW) { ZprobeErr(Srng); Srng = 0; break; } //error
          progressmfg(10,Srng); //goto TOP-Limit
          ZmUP(); while (digitalRead(TSense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveUP1(); }
          delay(250);
          if (digitalRead(TSense)!=LOW) { Srng = 5; break; } //error
          ZcountSet = Zcount; 
          ZcountSave = Zcount; // Save TOP to ZcountSave
          Srng++;
          break;
        
        case 2: // Move to BSense BOTTOM Limit
          progressmfg(11,Srng); //goto BOT-Limit
          ZmDN(); while (digitalRead(PSense)==HIGH && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveDN1(); }
          delay(250);
          if (digitalRead(BSense)!=LOW) { Srng = 5; break; } //not Bottom?
          ZcountSave = ZcountSave - Zcount; // Save Range Counts
          ZcountSet = Zcount; 
          Srng++; 
          break;
        
        case 3: // Got it! Do calculation here
          ZmaxRNG = (float)ZcountSave / Zscale; //inch OK
          Zzero = ZmaxRNG / 2; // current location
          Zreload();
          progressmfg(12,Srng); //goto MID Pos.
          Zcount = 0; Zmove2mid(); 
          Zcount = 0; ZcountSet = 0;     
          Srng++; 
          break;
        
        case 4: // RNG Range Setup Done
          Zcntr(1); Zshowr(2);
          progressmfg(13,Srng); //Range Setup DONE
          anyKey();
          Srng = 0;
          break;
        
        case 5: // RNG Processing error, Reload Defalult
          ZcountSet = Zcount;
          ZprocessErr(Srng); 
          ZerrReload();
          Srng = 0;
          break;
      }      
      break;    

//CAL =OK= 3/22
    case CAL:   // Z-CALIBRATION Auto Calibration 1-Inch Block using PSense Default HiRES
      pageTitle();
      digitalWrite(EDM,EDMoff); //ensure EDM is OFF
      
      switch(Scal) {
        case 0: // Disp info Run to start
          runWinch();
          mfg = "* Clear work surface"; printmfg(0,1);
          mfg = "for Base-ZERO Detect"; printmfg(0,2);
          progressmfg(0,Scal);  //[RUN] key to Start
          break;
        
        case 1: // seeking Zero
          if (digitalRead(PSense)==LOW) {
            ZprobeErr(Scal); Scal=0;  //error start over
            break; 
          }
          // switch to HiRES mode
          Zedm = mHiRES; Zreload();
          pageTitle();
          
          // CAL start here          
          progressmfg(17,Scal);  //seek DN Base Ref0        
          // seek DN Work-Zero Error if not stop by Psense or (ZMMax-Zcount) Less that 120% of Test Block 
          ZmDN(); while (digitalRead(PSense)==HIGH && Zcount>ZcountMin && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveDN1(); } 
          if (digitalRead(PSense)==HIGH) { ZprocessErr(Scal); Scal=0; break; }      
          ZcountSave = Zcount; ZcountSet = Zcount;
          //Fine-tune zero
          ZmUP(); while ( Zcount < ZcountSave+ZcountGap ) { ZmoveUP1(); }
          while (digitalRead(PSense)==HIGH && Zcount>ZcountMin && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveDN1(); runWinch(); } 
          if (digitalRead(PSense)==HIGH) { ZprocessErr(Scal); Scal=0; break; } 
          ZcountSave = Zcount; // Base Zero Save
          ZcountSet = Zcount; 
          //found zero, prep for next
          dispclear(2);
          mfg = "* Base-ZERO Detected"; printmfg(0,1);
          //progressmfg(10, Scal);  //goto TOP Position
          progressmfg(24, Scal);  //goto Park Position
          ZcountCHK = Zcount + (float)1.5 * Zcalblock * Zscale;
          if (ZcountCHK>ZcountMax) ZcountCHK = ZcountMax;        
          ZmUP(); while (Zcount<ZcountCHK && digitalRead(TSense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveUP1(); }
          if (Zcount!=ZcountCHK ) { ZprocessErr(Scal); Scal=0; break; }  
          ZcountSet=Zcount;
          Scal++;
          break;  
        
        case 2: //Prep calibration
          runWready();
          //Display Current Range
            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);
          progressmfg(0,Scal);  //[RUN] to Start 
          break;
        
        case 3: //Do 1" Blk Calibration here
          progressmfg(18, Scal);  //seek DN 1-inch BLK, Error if not stop by Psense 
          ZmDN(); while (digitalRead(PSense)==HIGH && Zcount>ZcountSave && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveDN1(); } 
          if (digitalRead(PSense)==HIGH) { ZprocessErr(Scal); Scal=0; break; }      
          ZcountSet=Zcount;
          //Fine-tune 1" BLK
          ZmUP(); while ( Zcount < ZcountSet+ZcountGap ) { ZmoveUP1(); }
          while (digitalRead(PSense)==HIGH && digitalRead(BSense)==HIGH && digitalRead(ESense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveDN1(); runWinch(); } 
          if (digitalRead(PSense)==HIGH) { ZprocessErr(Scal); Scal=0; break; }
          ZcountSet = Zcount; // 1" BLK
          ZcountSave = ZcountSet-ZcountSave;  // New Scale
          // check Scale limits
          if (ZcountSave < (Zscale*(100-ZscaleRange)/100) || (ZcountSave > Zscale*(100+ZscaleRange)/100)) { ZprocessErr(Scal); Scal=0; break; }
          //1-inch Mark Found
          progressmfg(19,Scal);  //Calibration DONE 
          ZmUP(); while (Zcount<ZcountMax && digitalRead(TSense)==HIGH && digitalRead(kpage)==HIGH && digitalRead(kdn)==HIGH && digitalRead(kup)==HIGH) { ZmoveUP1(); }
            ZcountSet = Zcount;
            mfgtemp = String(ZcountSave);
            mfg="";
            for (int j=6; j>mfgtemp.length(); j--) mfg += " ";
            mfg += (mfgtemp); mfg +=" ct/Inch  NEW ";  printmfg(0,2);
          delay(disptime);
          Scal++;
          break;
        
        case 4: //CAL End
          progressmfg(14, Scal);  //[S]Save [RUN]Skip
          anyKey();
          if (digitalRead(kset)==LOW) {
            if (Zedm==0) { 
              ZfastCAL = ZcountSave; 
            } else { 
              ZhiresCAL = ZcountSave;
              ZfastCAL = ZhiresCAL / 2;
            }
            Zreload();
            ZcountSet=ZcountMax;
            anyKey();        
          }
          Scal=0;
          break;
      }
      break;

//INF =OK= 3/22
      case INF:   //Z Info scanning Display
        pageTitle(); runWinch(); Zstop=0;
        if ((Sinf==0) || (Sinf>13)) Sinf=1;
          switch(Sinf) { 
            case 1: Zinfos(1); Zshows(2); Zcnts(3); break; // Scale @1-Inch
            case 2: Zinfox(1); Zshowx(2); Zcntx(3); break; // Prox Val
            case 3: Zinfog(1); Zshowg(2); Zcntg(3); break; // Gap Val
            case 4: Zinfoe(1); Zshowe(2); Zcnte(3); break; // End Val
            case 5: Zinfor(1); Zshowr(2); Zcntr(3); break; // Range (Machine)
            case 6: ZinfoR(1); ZshowR(2); ZcntR(3); break; // wRange (Work)
            case 7: Zinfoz(1); Zshowz(2); Zcntz(3); break; // Zero-Ref
            case 8: ZinfoT(1); ZshowT(2); ZcntT(3); break; // wTop-Max
            case 9: ZinfoE(1); ZshowE(2); ZcntE(3); break; // wEnd
            case 10: ZinfoB(1); ZshowB(2); ZcntB(3); break; // wBot-Min
            case 11: ZinfoC(1); ZshowC(2); ZcntC(3); break; // Current
            case 12: ZinfoF(1); ZshowF(2); ZcntF(3); break; // Finish
          }
        break;

//TEST
    case TEST:  // Z-Speed Test Panel display, Page to Exit
      pageTitle(); runWspeed(); Zstop=0;
      PortTest();
      break;

//VER =OK=
    case VER:   // Display Version
      showFW();
      break;  //VER     
        
  } //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
/* SUB --------------------------------------------------------------------*/
/*
/* Key Check --------------------------------------------------------*/
/// Clear any Key Press
void noKey() {
  do {} while (digitalRead(kpage)==LOW || digitalRead(krun)==LOW || digitalRead(kset)==LOW || digitalRead(kup)==LOW || digitalRead(kdn)==LOW);
}

/// Check AnyKey Press
void anyKey() {
  do {} while (digitalRead(kpage)==HIGH && digitalRead(krun)==HIGH && digitalRead(kset)==HIGH && digitalRead(kup)==HIGH && digitalRead(kdn)==HIGH);
}

/* Zcounts ------------------------------------------------------*/
/// 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 = ZhiresDefault;
    Ain7 = Ain7/2;
  } else { 
    Zscale = ZfastDefault;
  }
}

/// Z Default Reload Works Setup Default
void Zdefault() {
  ZfastDefault = ZhiresDefault / 2;
  Zcurrent = (float)Zcount/Zscale;
  if (Zedm==mFAST) { 
    Zscale = ZfastDefault; 
    digitalWrite(Zs3sw,Zfast); 
  } else { 
    Zscale = ZhiresDefault; 
    digitalWrite(Zs3sw,Zhires); 
  }
  ZcountRange = (float)ZmaxDefault*Zscale; //Range Count
  Zzero = ZmaxDefault/2;  //Default MID Pos as BSense Zero in inch
  Zupdate();
}

/// Z Reload Check
void Zreload() {
  Zcurrent = (float)Zcount/Zscale;
  if (Zedm==mFAST)  {
    digitalWrite(Zs3sw,Zfast);
    if (ZfastCAL==0) Zscale=ZfastDefault; else Zscale=ZfastCAL;
  } else {
    digitalWrite(Zs3sw,Zhires);
    if (ZhiresCAL==0) Zscale=ZhiresDefault; else Zscale=ZhiresCAL;
  }
  if (ZmaxRNG==0) {ZcountRange = (float)ZmaxDefault*Zscale;} else {ZcountRange = (float)ZmaxRNG*Zscale;}
  Zupdate();  
}

/// Z Counts Re-calculation base on RNG, CAL, wZero
void Zupdate() {
  ZcountEnd = (float)Zend * Zscale; //Value
  ZcountGap = (float)Zgap * Zscale; //Value
  ZcountProx = (float)Zprox * Zscale; //Value
  ZcountZero = (float)Zzero * Zscale; //Value
  Zcount = (float)Zcurrent * Zscale; //Value
  ZcountSet = Zcount;
  ZcountMMax = ZcountRange - ZcountZero; //TSense counts
  ZcountMax = ZcountMMax - ZcountGap; //work Top-Limit
  ZcountMMin = ZcountMMax - ZcountRange; //BSense counts
  ZcountMin = ZcountMMin + ZcountGap;  // Work Bot-Limit
}

/* Z-ERROR >>------------------------------------------------------------------------*/
/// Z Error Stop
void ZerrStop() {
  Zstop=1;
  ZerrReload();
}

/// Z-Drive ERR Reload Default (in HiRES mode)
void ZerrReload() {
  lcd2.clear();
  mfg = "E R R O R !"; printmfg(5,1);
  if (ZmaxRNG==0 && ZhiresCAL==0) mfg = "Reload Default"; else mfg = "Reload Setting"; 
  printmfg(3,2);
  if (ZmaxRNG==0 && ZfastCAL==0) Zdefault(); else Zreload();
  delay(disptime);
}

/// Process Error Reload 17-chr return to 'j'
void ZprocessErr(int j) {
  mfg = "!Processing Error ";
  printmfg(2,3);
  delay(disptime);
}

/// Z Error Reboot
void ZerrReboot() {
  lcd2.clear();
  lcd2.setCursor(1,1);lcd2.print("System Error Detect.");
  lcd2.setCursor(3,3); lcd2.print("R E B O O T !!!");
  delay(disptime);
  resetBoot();
}

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

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

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

/// Z move to Mid
void Zmove2mid() {
  ZcountSet = (ZcountMMax - ZcountMMin) / 2;
  Zcount = 0;
  do { Z2countSet(); } while (Zcount != ZcountSet );
}
/// Z move to Zmax
void Zmove2max() {
  ZcountSet = ZcountMax;
  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) { 
    ZmUP(); while (Zcount<ZcountSet && digitalRead(TSense)==HIGH && digitalRead(kset)==HIGH) { ZmoveUP1(); }
  } else {
    ZmDN(); while (Zcount>ZcountSet && digitalRead(PSense)==HIGH && digitalRead(BSense)==HIGH && digitalRead(kset)==HIGH && digitalRead(ESense)==HIGH) { ZmoveDN1(); }
  }
  if (Zcount != ZcountSet) ZcountSet = Zcount;
}

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

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

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

/// Zmode Display 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 ("                    "); 
}

/// A7 POT Set ZCLK Speed
void getZin7() {
  Ain7 = analogRead(A7pulse)/2; //Read once
  Ain7 += pulseMin; 
}

/// Status w/ ZCLK Freq 8-chr
void runWfreq() {
  mfg = "";
  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 + 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 is Ready
void runWready() {
  lcd2.setCursor(6,0); lcd2.print("Ready! ");
}

/// 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);
}

/// Page Title per (6)+<8>+(6)
/// 
void pageTitle() {
  lcd2.setCursor(0,0); lcd2.print(Zpagemsg[Zpage]); //6-chr
  mfg=""; lcd2.setCursor(14,0);
  if (Zpage==RUN)  {
    mfg += (Zedmmsg[Zedm]);
  } else {
    if (Zedm==mFAST) mfg += (Zedmmsg[mFAST]); else mfg += (Zedmmsg[mHiRES]);
  }
  lcd2.print(mfg);
}

/// Z-Mode MSG on Line#j
void Zmodedisp(int j) {
  switch(Zedm) {
    case mFAST:
    case mHiRES:
      mfg = "[UP]=Pause  [DN]=End"; printmfg(0,j);
      break;
    case mZEND:
      ZshowE(j);     
      break;
  }
}

/// Z-Resolution on Line#j
void Zresdisp(int j) {
  mfg="Z-Resolution: ";
  if (digitalRead(Zs3sw)==Zfast) mfg+= "FAST  "; else mfg+= "HiRES ";
  lcd2.setCursor(0,j); lcd2.print(mfg);
}

/// Z Info Display in Line(j) Lower-case=Machine, Upper-case=from Work Zero, Unit=Counts
void Zinfos(byte j) { lcd2.setCursor(0,j); showInfo((Zscale),               "s Scale-Val"); }
void Zinfor(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMMax-ZcountMMin),"r Range-Val"); }
void ZinfoR(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMax-ZcountMin),  "R    wRange"); }
void Zinfot(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMMax),           "t Top-Limit"); }
void ZinfoT(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMax),            "T  wTop-Max"); }
void Zinfob(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMMin),           "b Bot-Limit"); }
void ZinfoB(byte j) { lcd2.setCursor(0,j); showInfo((ZcountMin),            "B  wBot-Min"); }
void Zinfox(byte j) { lcd2.setCursor(0,j); showInfo((Zprox*Zscale),         "x  Prox-Val"); }
void ZinfoX(byte j) { lcd2.setCursor(0,j); showInfo((ZcountProx),           "X wProx-Pos"); }
void Zinfoe(byte j) { lcd2.setCursor(0,j); showInfo((Zend*Zscale),          "e   End-Val"); }
void ZinfoE(byte j) { lcd2.setCursor(0,j); showInfo((ZcountEnd),            "E  wEnd-Pos"); }
void Zinfog(byte j) { lcd2.setCursor(0,j); showInfo((Zgap*Zscale),          "g   Gap-val"); }
void ZinfoG(byte j) { lcd2.setCursor(0,j); showInfo((ZcountGap),            "G  wGap-Pos"); }
void Zinfoz(byte j) { lcd2.setCursor(0,j); showInfo((ZcountZero),           "z  Zero-Ref"); }
void ZinfoC(byte j) { lcd2.setCursor(0,j); showInfo((Zcount),               "C  wCurrent"); }
void ZinfoF(byte j) { lcd2.setCursor(0,j); showInfo((ZcountFin),            "F   wFinish"); }

void Zshows(byte j) { lcd2.setCursor(0,j); showInMM((Zscale), "s"); }
void Zshowr(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMMax-ZcountMMin), "r"); }
void ZshowR(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMax-ZcountMin), "R"); }
void Zshowt(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMMax), "t"); }
void ZshowT(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMax), "T"); }
void Zshowb(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMMin), "b"); }
void ZshowB(byte j) { lcd2.setCursor(0,j); showInMM((ZcountMin), "B"); }
void Zshowx(byte j) { lcd2.setCursor(0,j); showInMM((Zprox*Zscale),"x"); }
void ZshowX(byte j) { lcd2.setCursor(0,j); showInMM((ZcountProx),"X"); }
void Zshowe(byte j) { lcd2.setCursor(0,j); showInMM((Zend*Zscale), "e"); }
void ZshowE(byte j) { lcd2.setCursor(0,j); showInMM((ZcountEnd), "E"); }
void Zshowg(byte j) { lcd2.setCursor(0,j); showInMM((Zgap*Zscale), "g"); }
void ZshowG(byte j) { lcd2.setCursor(0,j); showInMM((ZcountGap), "G"); }
void Zshowz(byte j) { lcd2.setCursor(0,j); showInMM((ZcountZero),"z"); }
void ZshowC(byte j) { lcd2.setCursor(0,j); showInMM((Zcount),    "C"); }
void ZshowF(byte j) { lcd2.setCursor(0,j); showInMM((Zcount),    "F"); }

void Zcnts(byte j) { lcd2.setCursor(0,j); showInCNT((Zscale), "s"); }
void Zcntr(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMMax-ZcountMMin), "r"); }
void ZcntR(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMax-ZcountMin), "R"); }
void Zcntt(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMMax), "t"); }
void ZcntT(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMax), "T"); }
void Zcntb(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMMin), "b"); }
void ZcntB(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountMin), "B"); }
void Zcntx(byte j) { lcd2.setCursor(0,j); showInCNT((Zprox*Zscale),"x"); }
void ZcntX(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountProx),"X"); }
void Zcnte(byte j) { lcd2.setCursor(0,j); showInCNT((Zend*Zscale), "e"); }
void ZcntE(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountEnd), "E"); }
void Zcntg(byte j) { lcd2.setCursor(0,j); showInCNT((Zgap*Zscale), "g"); }
void ZcntG(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountGap), "G"); }
void Zcntz(byte j) { lcd2.setCursor(0,j); showInCNT((ZcountZero),"z"); }
void ZcntC(byte j) { lcd2.setCursor(0,j); showInCNT((Zcount),    "C"); }
void ZcntF(byte j) { lcd2.setCursor(0,j); showInCNT((Zcount),    "F"); }

/// 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 ZlabInCNT(Inch,Label,Line#) in [NNN -x.xxx" #xxxxxxx]  //20-chr
void ZlabInCNT(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*Zscale,0);
  for (int j=7; j>mfgtemp.length(); j--) mfg += " ";
  mfg += mfgtemp;
  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);  
}

/// Display PORT Test results
void PortTest() {
  lcd2.setCursor(0,1); mfg="";
    if (digitalRead(kup)==LOW)  mfg+= " Zup "; else mfg+=" ... ";
    if (digitalRead(kdn)==LOW)  mfg+= " Zdn "; else mfg+=" ... ";
    if (digitalRead(kset)==LOW) mfg+= " Set "; else mfg+=" ... ";
    if (digitalRead(krun)==LOW) mfg+= " Run "; else mfg+=" ... ";
    lcd2.print(mfg);
  lcd2.setCursor(0,2); mfg="";
    if (digitalRead(TSense)==LOW) mfg+= " [T] "; else mfg+=" +++ ";
    if (digitalRead(BSense)==LOW) mfg+= " [B] "; else mfg+=" +++ ";
    if (digitalRead(ESense)==LOW) mfg+= " [E] "; else mfg+=" +++ ";
    if (digitalRead(PSense)==LOW) mfg+= " [P] "; else mfg+=" +++ ";
    lcd2.print(mfg);
  lcd2.setCursor(0,3); mfg="<zSP:"; //7 chr
    Aread=analogRead(A7pulse);
    if (Aread<1000) mfg+=" ";
    if (Aread<100) mfg+=" ";
    mfg+=String(Aread,0); mfg+="> PAGE=Exit";
    lcd2.print(mfg);
}

void showFW() {
  mfg = (info0); printmfg(0,0);
  mfg = (info1); printmfg(0,1);
  mfg = (info2); printmfg(0,2);
  mfg = (info3); printmfg(0,3);
}

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

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