Coding Style

Read this, it'll make the code easier to follow.
When you write a large or complex piece of code then years later something needs adding, changing, or your CEO heard about a new language from one of his golfing buddies and he just must have it (this used to happen a lot).  
Please see the Example code below.
I like top down coding.
BTW: you'll often see the word demon spelled daemon, its still pronounced demon.   A lot of the guys who wrote Unix were from the UK.   In Europe, when a long vowel sound is needed, they use double vowels with the second vowel having the long sound.   This was seen mostly in proper nouns, but I also seee it in other words.   Like the proper noun Diesel (long 'E' sound, you don't sound the 'I'>


Header Comment Block
I usually put a header comment at the top of the listing, before the includes.   For Arduino sketches, I note what sensors or peripheral chips the program is using.   Use a distinctive function block at the beginning of the function code, again, makes it easy to spot.   Put the funcion name on the next line after the function type.
#defines
ALL CAPS
Global Variables
Start with UPPER CASE, local variables start with lower case
Comments
ALL CAPS, right justified
Indents
Two chars is too little, 8 too much, 4 is just about right.
Left curley brace
Should be at beginning of a line (with correct indent), and is a great place for a comment stating what the enclosed block does.
Wrap a line
Make sure the 1st char on the wrapped line is punctuation. It is easier to understand if you don't jam it together, your looking for the logic flow.
I don't like to type the word 'unsigned'
typedef it away to 'ulong' for instance.
main (or setup & loop)
Always 1st in the listing, followed by the fuctions it calls, thats why God invented PROTOTYPES.
Defines, Global Variables, & Prototypes
Should be at the beginning of the listing.   That way you know were to look when you encounter a reference to one down in the code.   Note: If you need to build a Global variable with function pointers swap the variables and prototype sections.
Function calls.
I always put the left parin next to the function name, followed by a space.   It makes the call easier to spot when you're trying to figure out whats going on in the code.   Like: if( barf > 16 )
case statements
follow with the left curley under the 's' in switch with a comment.   Indent the case, then indent again for each line in the case till the break;   All cases should be at the same indent, all lines within the case should be at the same indent (one more than the case itself).

Example code: DustAuto.ino
/***********************************************************************
DUST COLLECTION AUTOMATION:  
PARTS TAKEN FROM BOB CLAGETT ON I LIKE TO MAKE STUFF (ILIKETOMAKESTUFF.COM)
I/O DEVICE: LCDKEYPAD (DATA TERM)
HDW: PCA9685 I2C PWM Servo driver, MG996R Servos, 74HC4051 Analog Mux, 
CURRENT SENSOR: ACS712-30A (66Ms/A), AND A SOLID STATE RELAY 
TOOLS NUMBERED 1 - 8, NO ZERO 
***********************************************************************/
#include <Wire.h>
#include <Adafruit_PWMServoDriver.h>
#include <SoftwareSerial.h>
 
/************************** DEFINES ************************************/
#define AUTODLY    20000                              // AUTO MODE LOCK TIME
#define DCRUN      11                               // DUST COLLECTOR GO PIN
#define DSPDLY     1000               // DELAY BETWEEN VOLT READING DISPLAYS
#define DUSTAVGCNT 5                // MUST HAVE DUSTAVGCNT RDNGS TO AVERAGE
#define DUSTBADS   3                  // MUST HAVE DUSTBADS DUSTMINS TO HALT
#define DUSTCHK    1000                        // TIME BETWEEN DUST REQUESTS
#define DUSTWAT    100                           // TIME WAIT FOR DUST DTCTR
#define DUSTMIN    8                // DUST BIN MUST HAVE MORE THAN 8" SPACE
#define ESC        92      // BACKSLASH, IDE, Serial DOESN'T SEND CNTRL CHRS
#define INSTRMAX   32                                       // INSTRING SIZE
#define MITERSAW   6                                         // MITERSAW NBR
#define MUXS0      7  // LSB                          // ANALOG MUX SELECT 0
#define MUXS1      6                                  // ANALOG MUX SELECT 1
#define MUXS2      5   // MSB                         // ANALOG MUX SELECT 2
#define NBRTOOLS   8                                      // NUMBER OF TOOLS 
#define NBRVALVES  8                                     // NUMBER OF VALVES
#define PWM_ADDR   0X40                            // SERVO CONTOROL ADDRESS
#define V8WAT      5000                      // PLANER VALVE WAIT TIME 5 SEC
#define RDGTHLD    200          // DIGITAL READING THRESHOLD @ 4.887 MV/INCR
#define DDRX       3                                   // SOFTWARE SERIAL RX
#define DDTX       4                                   // SOFTWARE SERIAL TX
#define V8RX       8                                    // VALVE-8 SERIAL RX
#define V8TX       9                                    // VALVE-8 SERIAL TX
#define SNSRDLY    20                                    // SAMPLE TIME 20MS
#define SRVODLY    525       // MG996R MVS 60deg in .17 SEC, SERVO ACT DELAY
#define SPINUP     1000                                // DC SPIN UP TIME MS
#define SPINDOWN   3000                     // DUST COLLECTOR SPIN DOWN TIME
#define SRVOBUMP   5                             // VALVE SERVO INCR OR DECR
#define NL         10
#define OPN        0                                            // VALVE CCW
#define CLOS       1                                             // VALVE CW
#define ZIN        A7                                           // SENSOR IN
enum Modes { IDLE, MANUAL, DSPRDG, AUTO, SETSRVO, MULTVLV, DUSTBINHALT };
typedef unsigned long ulong;
#define ssL(str,ndx) for( byte x = 0; x < ndx +1; x++ ) str[x] = str[x +1];

/************************** VARIABLES ************************************/
Adafruit_PWMServoDriver Servo = Adafruit_PWMServoDriver();
SoftwareSerial Dust( DDRX, DDTX );            // DUST DETECTOR SERIAL RX, TX
SoftwareSerial V8( V8RX, V8TX );           // PlanerValve (V8) SERIAL RX, TX
bool DcOnFlg = false, Rs232 = false, DstOnScrn = false;
bool V8Req = false; 
char *ToolNam[NBRTOOLS+1] = { "0","Sander","BandSaw","Free-1","TableSaw"
    ,"Free-2","MiterSaw","Router","Planer" };            // TOOL NAMES ARRAY
short BasVal[NBRTOOLS +1], RdgTool = 0, VlvOc = CLOS;
int DustSpc, DustCnt =0, DustAcc =0, DustBads =0;       // DUST RELATED VARS
int VlvVals[NBRVALVES +1][2] = {{0,0},      // OPN,CLOS VALVE, NO VALVE ZERO
 {160,480}, {150,445}, {160,450}, {180,425}, {160,450},{125,435},{150,440}
// SANDER     BANDSAW    FREE-1     tableSAW   FREE-2    MITERSAW  ROUTER
  ,{190,465}, }; // MITERSAW
ulong NxtDsp =0, AutoTime =0, DustTim = 0;
char CurPos[8] = { ESC,'[','0',';','0','0','f',0};  // POSIT CURSOR, ROW,COL
char ClrScrn[6] = { ESC,'[','2','j',0};                      // CLEAR SCREEN
char Hdr[] = {"   Dust Collector\n"}, DstChrs[18];
char InStr[INSTRMAX], *VlvPos[2] = {"Open","Close"};
byte InNdx =0, Mode = IDLE, ActvTool = 0, ActVlv = 0, SsNdx =0;

/************************** PROTOTYPES ************************************/
void cancel( byte noPnt );                          // CANCEL ALL OPERATIONS
bool chkAmpChg( int tool );                      // CHECK FOR CURRENT CHANGE
void chkDustBin();                                // PROCESS DUST BIN DEPTHS
void chkTools();                                    // CHECK FOR ACTIVE TOOL
void clrScrn();                                  // CLEAR DATA TERMINAL SCRN
void cycVlvs();                                          // CYCLE ALL VALVES
void dcOn(),dcOff();                                // DUST COLLECTOR ON OFF
void dspDust( int dust );                             // DISPLAY DUST STATUS
void dspIRdg( char chr ), dspIRdgSu();             // DISPLAY ISNSR READINGS
void doChrs();                                          // PROCESS KBD CHARS
void dustHalt( byte row, byte col, char* msg);                  // DUST HALT
char getChr();                                      // GET A CHAR FROM INSTR
int getDust();                  // SOFTWARE SERIAL INTERRUPT SERVICE ROUTINE
void multVlv( char chr ), multVlvSu();               // OPEN MULTIPLE VALVES
short opnVlv( uint8_t num), clsVlv( uint8_t num, byte prnt); // OPN/CLOS VLV
void posCur( byte row, byte col );                     // SET CURSOR POSTION
short  reqV8( char cmd );                    // SEND REQUEST TO PLANER VALVE
int  rdSnsr( short snsr);                                   // READ A SENSOR
void runTool( char chr );                                    // START A TOOL
void serialEvent();                               // RS232 INTERRUPT ROUTINE
void setSrvo( char chr), setSrvoSu();                    // SET SERVO VALUES
short v8Fail();

/*F******************************************************************
*
**********************************************************************/
void 
setup()
{ 
    int   ndx;

    Serial.begin( 9600 );             // START SERIAL I/O FROM DATA TERMINAL
    Serial.print( Hdr );                        // PRINT HEADER ON DATA TERM
    Dust.begin( 9600 );               // START SFTW SERIAL I/O FROM DUST BIN
    V8.begin( 9600 );                  // START SFTW SERIAL I/O WITH VALVE 8
    pinMode( DCRUN, OUTPUT);  // I/O PIN OUTPUT FOR STARTING 2HP DUST BLOWER
    Servo.begin();
    Servo.setPWMFreq( 50 );                           // SET PWM FREQ = 50HZ
    delay( 1000 );
    pinMode( MUXS2, OUTPUT );                 // DEFINE ANALOG OUTPUT TO MUX
    pinMode( MUXS1, OUTPUT );                 // DEFINE ANALOG OUTPUT TO MUX
    pinMode( MUXS0, OUTPUT );                 // DEFINE ANALOG OUTPUT TO MUX
    for( ndx =1; ndx <= NBRTOOLS; ndx++)
        BasVal[ndx] = rdSnsr( ndx ); // SAVE BASE SNSR RDGS, FOR VOLT CMPRSN
    for( ndx =1; ndx <= NBRVALVES; ndx++)
        clsVlv( ndx, 0 );                                // CLOSE ALL VALVES
}
/*F******************************************************************
*
**********************************************************************/
void 
loop()
{
    if( Rs232 == true )
        doChrs();               // CHAR ARRIVED ON SERIAL I/O FROM DATA TERM
    if( millis() > DustTim )
          chkDustBin();                              // CHECK DUST BIN DEPTH
    if( Mode == DUSTBINHALT )
        return;                   // DUST BIN FULL, ONLY CANCEL WILL RESTART
    chkTools();                        // SEE IF ANY TOOL'S MOTOR IS RUNNING
    if( (Mode == DSPRDG) )
        dspIRdg( 9 );                  // PASS CHAR TO DISPLAY ISNSR VOLTAGE
    if( (Mode == SETSRVO) )
        setSrvo( 9 );                           // PASS CHAR TO ADJUST SERVO
}
/*F******************************************************************
* DUSTBIN DEPTH MESSAGE PROCESSING, AFTER DUSTAVGCNT 
**********************************************************************/
void 
chkDustBin()
{
    int      dust;

    if((Mode == DSPRDG) || (Mode == SETSRVO))
        return;
    if( (dust = getDust()) < 0 )
        return( dustHalt( 3, 0, "    No Dust "));
    if( (DustAcc > 0) && (DustCnt >= DUSTAVGCNT) )
    {                           // AVERAGE DUST DEPTH REPORTS FROM DETECTOR
        DustSpc = DustAcc / DustCnt;                   // UPDATE DUST SPACE
        dspDust( DustSpc );
        DustCnt = DustAcc = 0;               // RESET ACCUMULATOR AND COUNT
        if( (DustSpc <= DUSTMIN) && (DustSpc > 0) && (DcOnFlg == true) )
        {
            if( (Mode == DUSTBINHALT) || (++DustBads < DUSTBADS) )
                return;      // ALREADY HALT MODE, OR NOT ENOUGH BAD DEPTHS
            dustHalt( 0, 3, "Dust Bin Halt");  // DC RUNING & DUST BIN FULL
        }
        else
            DustBads = 0;          // ONE GOOD DUST READING RESETS DUSTBADS
    }
}
/*F******************************************************************
* CHECK FOR TOOL MOTOR RUNNING
**********************************************************************/
void 
chkTools()
{
    int     ndx, tool =0;
    ulong   now;

    if( (Mode != AUTO) && (Mode != IDLE) )
        return;            // DO NOT CHECK FOR I FLOW UNLESS IN AUTO OR IDLE
    now = millis();
    for( ndx =1; ndx <= NBRTOOLS; ndx++)
    {                   // SCAN THROUGH TOOLS & CHECK FOR INCREASED CUR FLOW
        if( chkAmpChg( ndx ))
        {                                                         // TOOL ON
            tool = ndx;
            Mode = AUTO;
            if( tool == MITERSAW )
                AutoTime = millis() + AUTODLY;           // MITERSAW RUNNING
            break;                                              // FOUND ONE
        }
    }
    if( (ActvTool == MITERSAW) && (now < AutoTime) )
        return;                    // TOOL STARTED, WAIT AUTODLY B4 CHANGING
    if( ActvTool == tool )
        return;                                   // SAME TOOL STILL RUNNING
    if( ActvTool && !tool )
        cancel( 0 );                          // NO ACTIVE TOOL AND DC IS ON
    else if( (tool != 0) && (tool != ActvTool) )
        runTool( tool );                                       // START TOOL 
}
/*F******************************************************************
* PROCESS KBD CHRS
* 0 CANCEL ALL
* 1-8 OPEN VALVE N, TURN BLOWER ON
* A     CYCLE ALL VALVES
* #n    TEST ISNSRS
* *n    SET SERVOS
**********************************************************************/
void
doChrs()
{
    char    chr;

    while( (chr = getChr()) != 0 )       // READ A CHAR FROM InStr, IF AVAIL
    {
        Rs232 = false;                              // CLEAR SERIAL I/O FLAG
        if( (Mode == DSPRDG) && (chr != '0'))
            return( dspIRdg( chr ) );     // PASS CHR TO DSPLY ISENSOR VOLTS
        if( (Mode == SETSRVO) && (chr != '0'))
            return( setSrvo( chr ));              // PASS CHR TO SERVO SETUP
        if( (Mode == MULTVLV) && (chr != '0'))
            return( multVlv( chr ));        // PASS CHR TO MULTI VALVE SETUP
        switch( chr )
        {
            case   '0':                          // CANCEL CURRENT OPERATION
                cancel( 0 );
                break;
            case   'A':                                  // CYCLE ALL VALVES
                cycVlvs();
                break;
            case   'B':
                multVlvSu();                         // OPEN MULTIPLE VALVES
                break;
            case '1' ... '8':            // MANUAL MODE, OPEN VALVE & RUN DC
                Mode = MANUAL;
                runTool( chr );
                break;
            case  '*':             // SPLAT, DISPLAY ISENSOR VOLTAGE READING
                dspIRdgSu();
                break;
            case  '#':                               // CRUNCH, SETUP SERVOS
                setSrvoSu();
                break;
            case  NL:                                         // GOT NEWLINE
                break;                                    // DISCARD NEWLINE
            default:
                Mode = IDLE;
                break;
        }
    }
}
/*F******************************************************************
* SETUP FOR DISPLAY ISNSR VOLTAGE READINGS, CALLED FROM doChrs()
**********************************************************************/
void
dspIRdgSu()
{
    if( DcOnFlg )
        dcOff();                                   // DC IS RUNNING, STOP IT
    Mode = DSPRDG;                               // SET DISPLAY READING MODE
    AutoTime =0;
    ActVlv = 0;
    VlvOc = CLOS;
    clrScrn();
    posCur( 0, 2 );
    Serial.print("Read Sensor");
    posCur( 1, 0 );
    Serial.print("Enter Tool Number");
}
/*F******************************************************************
* SETUP SERVO SETUP
**********************************************************************/
void
setSrvoSu()
{
    if( DcOnFlg )
        dcOff();    
    Mode = SETSRVO;
    clrScrn();
    delay( 200 ); 
    posCur( 0, 0 );
    Serial.print("    SERVO SETUP");
    posCur( 1, 0 );
    Serial.print("Enter Servo Number");
}
/*F******************************************************************
* RUN A MACHINE
**********************************************************************/
void
runTool( char chr )
{
    byte   tool;

    clrScrn();
    tool = (chr & 15);                    // STRIP OFF UPPER BITS OF CHR 0X3n
    posCur( 0, 6 );
    Serial.println( (String)ToolNam[tool] );       // ROW: 0, PRINT TOOL NAME
    if( (ActvTool > 0) && (ActvTool != tool))
    {                                                      // NEW ACTIVE TOOL
        if( DcOnFlg )
            dcOff();
        clsVlv( ActvTool, 1 );
    }
    ActvTool = tool;
    opnVlv( ActvTool );
    dcOn();
}
/*F******************************************************************
* CANCEL CURRENT OPERATION
**********************************************************************/
void
cancel( byte noPnt )
{
    short  ndx;

    if( !noPnt )
    {
        clrScrn();
        Serial.println("  Cancel Operation");
    }
    if( DcOnFlg )
        dcOff();                                         // DC IS ON, STOP IT
    for( ndx = 1; ndx <= NBRVALVES; ndx++ )
        clsVlv( ndx, 1 );                                 // CLOSE ALL VALVES
    Mode = IDLE;
    AutoTime =0;                      // CANCEL ANY TOOL RUNNING IN AUTO MODE
    RdgTool = ActvTool = 0;
    if( noPnt )
        return;
    clrScrn();
    Serial.print((String)Hdr );
}
/*F******************************************************************
* DISPLAY ISNSR VOLTAGE READINGS, CALLED FROM loop(), or doChrs()
**********************************************************************/
void
dspIRdg( char chr )
{
    short     tool, val;
    int32_t   now;
    
    if( (chr >= '1' ) && (chr <= '8' ))
    {                                                        // SET NEW TOOL
        RdgTool = (chr & 0X0F);                            // STRIP OFF 0X3n
        posCur( 1, 0 );                                      // CLEARS ROW 1
        Serial.print( (String)ToolNam[RdgTool] + " = " );
        NxtDsp = 0;                               // FORCE IMMEDIATE DISPLAY
    }
    now = millis();                                      // GET CURRENT TIME
    if((now > NxtDsp) && (RdgTool != 0))
    {                                   // TIME TO DISPLAY A CURRENT READING
        NxtDsp = (now + DSPDLY);             // SAVE TIME OF CURRENT DISPLAY
        val = rdSnsr( RdgTool );
        posCur( 1, 10 );                                    // ROW 1, COL 10
        Serial.println( val );                              // 4.88MV / INCR
    }
}
/*F******************************************************************
* CYCLE ALL VALVES TO REMOVE ANY LARGE SPLINTERS ETC.
**********************************************************************/
void
cycVlvs()
{
    char  tool, chr, buf[24];
    
    clrScrn();
    Serial.println("  Cycling Valves ");
    if( DcOnFlg )
        dcOff();    
    for( tool = 1; tool <= NBRVALVES; tool++ )
    {                                                      // FLAP ALL VALVES
        posCur( 1, 0 );                                       // ROW 2, COL 0
        Serial.println((String)ToolNam[tool] );
        opnVlv( tool );
        delay( SRVODLY );                       // WAIT FOR THE SERVO TO ACT
        clsVlv( tool, 1 );
        delay( SRVODLY );                       // WAIT FOR THE SERVO TO ACT
    }
    ActvTool = 0;
    clrScrn();
    Serial.print( Hdr );
}
/*F******************************************************************
* SERVO SETUP
**********************************************************************/
void
setSrvo( char chr )
{
    short     tool, val, rtn;
    int32_t   now;
    
    if( !ActVlv && (chr == 9))
        return;                         // CALLED FROM LOOP, NO ACTIVE VALVE
    if( !ActVlv && ((chr >= '1') && (chr <='8')))
    {                                                      // NEW SERVO NMBR
        ActVlv = chr & 15;                       // SET NEW ACTIVE VALVE NBR
          posCur( 1, 0 );                                      // CLEARS ROW 1
        Serial.print( (String)ToolNam[ActVlv] + "-" );   // LIKE "TableSaw-"
        NxtDsp = 0;                               // FORCE IMMEDIATE DISPLAY
    }
    else if( (chr >= '1') && (chr <= '8') )
    {
        switch( chr )
        {
            case '2':                                              // VAL UP
                VlvVals[ActVlv][VlvOc] += SRVOBUMP;
                break;
            case '4':                                           // OPEN VALVE
                VlvOc = OPN;         // VlvOc: VALVE SHOULD BE OPEN OR CLOSED
                break;
            case '6':                                          // CLOSE VALVE
                VlvOc = CLOS;        // VlvOc: VALVE SHOULD BE OPEN OR CLOSED
                break;
            case '8':                                               // VAL DN
                VlvVals[ActVlv][VlvOc] -= SRVOBUMP;
                break;
            default:
                break;
        }
        if( (ActVlv == 8) && (VlvOc == OPN) && ((rtn =reqV8( 'O' )) <0))
            v8Fail();
        else if( (ActVlv == 8) && (VlvOc== CLOS) && ((rtn =reqV8( 'C' )) <0))
            v8Fail();
        else
        {
            Servo.setPWM( ActVlv, 0, VlvVals[ActVlv][VlvOc]);    // SET SERVO
            delay( SRVODLY );                        // WAIT FOR SERVO TO ACT
        }
        NxtDsp = 0;                               // FORCE IMMEDIATE DISPLAY
    }
    now = millis();                                      // GET CURRENT TIME
    if((now > NxtDsp) && (ActVlv != 0))
    {
        NxtDsp = (now + DSPDLY);                // SAVE TIME OF NEXT DISPLAY
        posCur( 1, 10 );                                    // ROW 1, COL 10
        val = VlvVals[ActVlv][VlvOc];
        Serial.println( (String)VlvPos[VlvOc] + ":" +val ); // "Close: 450"
    }
}
/*F******************************************************************
* TURN DUST COLLECTOR ON
**********************************************************************/
void 
dcOn()
{
    if( Mode == DUSTBINHALT )
        return;
    digitalWrite( DCRUN, 1);
    posCur( 1, 13 );
    Serial.println("DC On ");
    DcOnFlg = true;
    DustCnt = DustAcc = 0;                         // RESET DUST ACCUM & CNT
    delay( SPINUP );
}
/*F******************************************************************
* TURN DUST COLLECTOR OFF
**********************************************************************/
void 
dcOff()
{
    posCur( 1, 3 );
    Serial.println("Stopping DC ");
    delay( 2000 );                           // WAIT FOR PLENUM/HOSE TO EMPTY
    digitalWrite( DCRUN, 0);
    delay( SPINDOWN );                                // WAIT FOR DC SPINDOWN
    posCur( 1, 0 );                             // ERASE ROW 1 TO END OF LINE
    DcOnFlg = false;
}
/*F******************************************************************
*  SAMPLING CURRENT SENSOR 1-7  (LO NIBBLE 0X0001 - 0X0111), SNSR 8 =0
**********************************************************************/
int
rdSnsr( short snsr )  // SNSR: 1 - 8
{
    int    rdVal, cnt, pin, msk =1, rtn;
    ulong   end, tmp;

    // S0:D7(LSB), S1:D6, S2:D5(MSB)  pin scans 7 - 5
    for( pin = MUXS0, msk = 1; pin > 4; --pin, msk <<=1 )
    {       // SET SENSOR # INTO MUX SEL0-SEL2 INPUTS, LSB to MSB, SNSR 8 = 0
        if( msk & snsr )    
            digitalWrite( pin, HIGH );        // WT HI PINS MUXS0,MUXS1,MUXS2
        else
            digitalWrite( pin, LOW );        // WT LOW PINS MUXS0,MUXS1,MUXS2
    }
    delay( 2 );                               // WAIT 2MS FOR SEL TO INP TIME
    end = millis() + SNSRDLY;              // SAVE CURRENT TIME + SAMPLE TIME
    for( cnt = tmp =0; millis() < end; cnt++ )
    {                                // NOISY SIGNAL, AVERAGE FOR SAMPLE TIME
        rdVal = analogRead( ZIN );                      // 0-1023 DIGITAL RDG
        tmp += rdVal;
    } // 175 - 180 samples IN 20 MS
    rtn = tmp / cnt;
    return( rtn );
}
/*F******************************************************************
* CHECK FOR AMPERAGE CHANGE ON A TOOL
**********************************************************************/
bool
chkAmpChg( int tool)
{
    int     rdg;

    rdg = rdSnsr( tool );       // READINGS RUN FROM OFF: 52-81, ON: 364-461
    if( rdg > RDGTHLD )  // NEW SENSOR IGNORE BASE (BasVal[tool] + RDGTHLD))
        return( true );                                    // RETURN TOOL ON
    else
        return( false );                                  // RETURN TOOL OFF
}
/*F******************************************************************
* GET CHAR FROM INSTR
**********************************************************************/
char
getChr()
{
    char   chr;

    if( InNdx == 0 )
        return( 0 );
    chr = InStr[--InNdx];
    ssL( InStr, InNdx );                                 // SHIFT INSTR LEFT
    if( InNdx == 0 )
        Rs232 = false;                                        // INSTR EMPTY
    return( chr );
}
/*F******************************************************************
* CLOSE A VALVE
**********************************************************************/
short 
clsVlv( uint8_t num,  byte prnt )
{
    short  rtn = 1;

    if( DcOnFlg )
        dcOff();
    VlvOc = CLOS;                       // SET CURRENT VALVE POSTION = CLOSED
    if( prnt )
    {
        posCur( 1, 0 );
        Serial.println((String)"Vlv "+num+" Clos" );
    }
    if( num == 8 )
        rtn = reqV8( 'C' );                          // REQUEST VALVE 8 CLOSE
    else
    {
        Servo.setPWM( num, 0, VlvVals[num][CLOS]);             // CLOSE VALVE
        delay( SRVODLY );                            // WAIT FOR SERVO TO ACT
    }
    Servo.setPWM( 15, 0, 400 );               // DISTRACT PCA9685 TO SERVO 15
    ActVlv = 0;
    if( prnt )
        posCur( 1, 0 );                                        // CLEAR ROW 1
}
/*F******************************************************************
* OPEN A VALVE
**********************************************************************/
short 
opnVlv( uint8_t num )
{
    int     rtn = 1;

    if( DcOnFlg )
        dcOff();
    VlvOc = OPN;
    posCur( 1, 0 );
    Serial.println((String)"Vlv "+num+" Opn");
    if( num == 8 )
        rtn = reqV8( 'O' );                           // REQUEST VALVE 8 OPEN
    else
    {
        Servo.setPWM( num, 0, VlvVals[num][OPN]);               // OPEN VALVE
        delay( SRVODLY );
    }
    ActVlv = num;
}
/*F******************************************************************
* RS-232 INTERRUPT SERVICE ROUTINE, DO NOT TARRY HERE
**********************************************************************/
void
serialEvent()
{                                         // RUNS EACH TIME (AFTER) LOOP RUNS
    int    val;
    char   chr;

    while( Serial.available()) // INTERRUPT SERVICE ROUTINE, DON'T TARRY HERE
    {
        chr = (char)Serial.read();                            // GET NEW CHAR
        if( chr < 32 )
            continue;                           // DISCARD NON_PRINtable CHAR
        if( InNdx >= INSTRMAX )
        {                                   // IN STR NDX TOO LARGE, RESET IT
            Serial.println("Overrun");
            break;
        }
        val = chr & 15;
        InStr[InNdx++] = chr;                      // ADD NEW CHAR TO InStr[]
        Rs232 = true;      // SET FLAG SO MAIN LOOP CAN DO SOMETHING ABOUT IT
    }
}
/*F******************************************************************
* REQUEST DEPTH FROM DUST DETECTOR, WAIT FOR RESPONSE
* DATA FROM THIS MAKES SERIOUS DECISIONS ABOUT SHUTING DOWN DC
**********************************************************************/
int
getDust()
{             // @ 9600 CHARS ARRIVE EVERY 1Ms
    int    val, dstVal;
    char   chr;
    ulong  end;

// Serial.println((String)"getDust: Bgn");
    Dust.listen();
    Dust.print( "D\n" );             // SEND DUST DEPTH REQUEST TO DUST DET
    DustSpc = 0;
    DustTim = millis() + DUSTCHK;     // SET TIME OF NEXT DUST DEPTH REQUEST
    val = dstVal =0;     // BELOW: < DustTim IN CASE DUSTDET DOESN'T RESPOND
    end = millis() + DUSTWAT;              // SET TIMER FOR DUST DET RESONSE
    while( millis() < end )
    {                              // LOOP HERE WHILE DUSTREQ & NOT DUSTTIME
        if( Dust.available() )
        {                                   // KEEP READING TIL FIND NEWLINE
            chr = Dust.read();                               // GET NEW CHAR
// Serial.println((String)"GotChr: "+chr);
            if( chr == '\n' )
            {                                         // END OF TRANSMISSION
                DustAcc += dstVal;                  // ACCUMULATE DUST DEPTH
                DustCnt++;                      // BUMP DUST READING COUNTER
                break;
            }
            if( (chr < '0') || (chr > '9')) 
                continue;                        // DISCARD NON NUMERIC CHAR
            val = (chr & 15); // CONVERT ASCII TO BIN: '2' (0X32) & 0X0F = 2
            dstVal = (dstVal * 10) + val;       // ACCUM DEC DUST DEPTH VALS
        }
    }
// Serial.println((String)"getDust: End, dstVal: "+dstVal);
    if( millis() > end )  
        return( -1 );
    return( dstVal );
}
/*F******************************************************************
* DUST HALT
**********************************************************************/
void 
dustHalt( byte row, byte col, char *msg)
{
    // DC RUNING & DUST BIN FULL, STOP EVERYTHING
    Mode = DUSTBINHALT;                       // ADVERTISE WHY WE'RE SHUTDOWN
    clrScrn();
    posCur( row, col );                                 // SET CURSOR POSTION
    Serial.println( (String)msg );
    cancel( 1 );                                           // STOP EVERYTHING
}
/*F******************************************************************
* OPEN MULTIPLE VALVE SETUP
**********************************************************************/
void
multVlvSu()
{
    if( DcOnFlg )
        dcOff();    
    Mode = MULTVLV;
    clrScrn();
    delay( 200 ); 
//    posCur( 0, 0 );
//    Serial.print("OPEN ANOTHER VALVE");
    posCur( 1, 0 );
    Serial.print("Enter Valve Number");
    dcOff();
}
/*F******************************************************************
*     OPEN MULTIPLE VALVES 
**********************************************************************/
void 
multVlv( char chr )                                 // OPEN MULTIPLE VALVES
{
    short   tool;

    if( (chr >= '1' ) && (chr <= '8' ))
    {                                                        // SET NEW TOOL
        Mode = MANUAL;
        tool = (chr & 0X0F);                            // STRIP OFF 0X3n
        posCur( 1, 0 );                                      // CLEARS ROW 1
        Serial.print( (String)"+ "+ToolNam[tool] );
        opnVlv( tool );
        dcOn();
    }
    else
        cancel( 0 );
}
/*F******************************************************************
* POSITION CURSOR Esc[r;ccf   SET CURSOR TO ROW COL
**********************************************************************/
void 
posCur( byte row, byte col )
{
    char    str[8];

    strcpy( str, CurPos );
    sprintf( &str[2], "%1d;%02df", row, col ); 
    Serial.print( str );
    delayMicroseconds( 200 );
}
/*F******************************************************************
* CLEAR SCREEN
**********************************************************************/
void 
clrScrn()
{
    Serial.print( ClrScrn );
    delay( 200 ); 
    DstOnScrn = false;
}
/*F******************************************************************
* RUN A MACHINE
**********************************************************************/
void
dspDust( int dustSpc )
{
    char  buf[32];

    if( !DstOnScrn )
    {                                   // NEED TO REPRINT "Dust Space"
        posCur( 3, 0 );                           // SET CURSOR POSTION
        Serial.print( (String)"Dust Space: ");
        DstOnScrn = true;
    }
    posCur( 3, 12 );
    sprintf( buf, "%02u\"", dustSpc );
    Serial.print( (String)buf );
}
/*F******************************************************************
* SEND PLANER VALVE (V8) OPEN/CLOSE REQUEST
 RETURN: 1 = OK, else -1
**********************************************************************/
short
reqV8( char req )                              // REQ IS UC LETTER 'O' or 'C'
{
    ulong  end;
    char   chr, resp =0;
    short  rtn = 1;

    V8.listen();
    V8.println((String)req );                    // SEND PLANER VALVE REQUEST
    end = millis() + V8WAT;             // SET TIMER FOR PLANER VALVE RESONSE
    while( millis() < end )
    {                                   // LOOP HERE WHILE PLANER VALVE MOVES
        if( V8.available() )
        {                                    // KEEP READING TIL FIND NEWLINE
            chr = V8.read();              // GET RESPONSE: 'O' =OK, 'F' =FAIL
            if( chr == NL )
                return( rtn );                             // END OF RESPONSE
            if( (chr != 'O') && (chr != 'K') )
                rtn = -1;
        }
    }
    return( -1 );                                    // TIMEOUT OR FAILED REQ
}
/*F******************************************************************
* 
**********************************************************************/
short
v8Fail()
{
    posCur( 2, 0 );
    Serial.println((String)"V 8 Failed");
    cancel( 0 );
    return( -1 );
}/***********************************************************************
DUST COLLECTION AUTOMATION:  
PARTS TAKEN FROM BOB CLAGETT ON I LIKE TO MAKE STUFF (ILIKETOMAKESTUFF.COM)
I/O DEVICE: LCDKEYPAD (DATA TERM)
HDW: PCA9685 I2C PWM Servo driver, MG996R Servos, 74HC4051 Analog Mux, 
CURRENT SENSOR: ACS712-30A (66Ms/A), AND A SOLID STATE RELAY 
TOOLS NUMBERED 1 - 8, NO ZERO 
***********************************************************************/
#include 
#include 
#include 
 
/************************** DEFINES ************************************/
#define AUTODLY    20000                              // AUTO MODE LOCK TIME
#define DCRUN      11                               // DUST COLLECTOR GO PIN
#define DSPDLY     1000               // DELAY BETWEEN VOLT READING DISPLAYS
#define DUSTAVGCNT 5                // MUST HAVE DUSTAVGCNT RDNGS TO AVERAGE
#define DUSTBADS   3                  // MUST HAVE DUSTBADS DUSTMINS TO HALT
#define DUSTCHK    1000                        // TIME BETWEEN DUST REQUESTS
#define DUSTWAT    100                           // TIME WAIT FOR DUST DTCTR
#define DUSTMIN    8                // DUST BIN MUST HAVE MORE THAN 8" SPACE
#define ESC        92      // BACKSLASH, IDE, Serial DOESN'T SEND CNTRL CHRS
#define INSTRMAX   32                                       // INSTRING SIZE
#define MITERSAW   6                                         // MITERSAW NBR
#define MUXS0      7  // LSB                          // ANALOG MUX SELECT 0
#define MUXS1      6                                  // ANALOG MUX SELECT 1
#define MUXS2      5   // MSB                         // ANALOG MUX SELECT 2
#define NBRTOOLS   8                                      // NUMBER OF TOOLS 
#define NBRVALVES  8                                     // NUMBER OF VALVES
#define PWM_ADDR   0X40                            // SERVO CONTOROL ADDRESS
#define V8WAT      5000                      // PLANER VALVE WAIT TIME 5 SEC
#define RDGTHLD    200          // DIGITAL READING THRESHOLD @ 4.887 MV/INCR
#define DDRX       3                                   // SOFTWARE SERIAL RX
#define DDTX       4                                   // SOFTWARE SERIAL TX
#define V8RX       8                                    // VALVE-8 SERIAL RX
#define V8TX       9                                    // VALVE-8 SERIAL TX
#define SNSRDLY    20                                    // SAMPLE TIME 20MS
#define SRVODLY    525       // MG996R MVS 60deg in .17 SEC, SERVO ACT DELAY
#define SPINUP     1000                                // DC SPIN UP TIME MS
#define SPINDOWN   3000                     // DUST COLLECTOR SPIN DOWN TIME
#define SRVOBUMP   5                             // VALVE SERVO INCR OR DECR
#define NL         10
#define OPN        0                                            // VALVE CCW
#define CLOS       1                                             // VALVE CW
#define ZIN        A7                                           // SENSOR IN
enum Modes { IDLE, MANUAL, DSPRDG, AUTO, SETSRVO, MULTVLV, DUSTBINHALT };
typedef unsigned long ulong;
#define ssL(str,ndx) for( byte x = 0; x < ndx +1; x++ ) str[x] = str[x +1];

/************************** VARIABLES ************************************/
Adafruit_PWMServoDriver Servo = Adafruit_PWMServoDriver();
SoftwareSerial Dust( DDRX, DDTX );            // DUST DETECTOR SERIAL RX, TX
SoftwareSerial V8( V8RX, V8TX );           // PlanerValve (V8) SERIAL RX, TX
bool DcOnFlg = false, Rs232 = false, DstOnScrn = false;
bool V8Req = false; 
char *ToolNam[NBRTOOLS+1] = { "0","Sander","BandSaw","Free-1","TableSaw"
    ,"Free-2","MiterSaw","Router","Planer" };            // TOOL NAMES ARRAY
short BasVal[NBRTOOLS +1], RdgTool = 0, VlvOc = CLOS;
int DustSpc, DustCnt =0, DustAcc =0, DustBads =0;       // DUST RELATED VARS
int VlvVals[NBRVALVES +1][2] = {{0,0},      // OPN,CLOS VALVE, NO VALVE ZERO
 {160,480}, {150,445}, {160,450}, {180,425}, {160,450},{125,435},{150,440}
// SANDER     BANDSAW    FREE-1     tableSAW   FREE-2    MITERSAW  ROUTER
  ,{190,465}, }; // MITERSAW
ulong NxtDsp =0, AutoTime =0, DustTim = 0;
char CurPos[8] = { ESC,'[','0',';','0','0','f',0};  // POSIT CURSOR, ROW,COL
char ClrScrn[6] = { ESC,'[','2','j',0};                      // CLEAR SCREEN
char Hdr[] = {"   Dust Collector\n"}, DstChrs[18];
char InStr[INSTRMAX], *VlvPos[2] = {"Open","Close"};
byte InNdx =0, Mode = IDLE, ActvTool = 0, ActVlv = 0, SsNdx =0;

/************************** PROTOTYPES ************************************/
void cancel( byte noPnt );                          // CANCEL ALL OPERATIONS
bool chkAmpChg( int tool );                      // CHECK FOR CURRENT CHANGE
void chkDustBin();                                // PROCESS DUST BIN DEPTHS
void chkTools();                                    // CHECK FOR ACTIVE TOOL
void clrScrn();                                  // CLEAR DATA TERMINAL SCRN
void cycVlvs();                                          // CYCLE ALL VALVES
void dcOn(),dcOff();                                // DUST COLLECTOR ON OFF
void dspDust( int dust );                             // DISPLAY DUST STATUS
void dspIRdg( char chr ), dspIRdgSu();             // DISPLAY ISNSR READINGS
void doChrs();                                          // PROCESS KBD CHARS
void dustHalt( byte row, byte col, char* msg);                  // DUST HALT
char getChr();                                      // GET A CHAR FROM INSTR
int getDust();                  // SOFTWARE SERIAL INTERRUPT SERVICE ROUTINE
void multVlv( char chr ), multVlvSu();               // OPEN MULTIPLE VALVES
short opnVlv( uint8_t num), clsVlv( uint8_t num, byte prnt); // OPN/CLOS VLV
void posCur( byte row, byte col );                     // SET CURSOR POSTION
short  reqV8( char cmd );                    // SEND REQUEST TO PLANER VALVE
int  rdSnsr( short snsr);                                   // READ A SENSOR
void runTool( char chr );                                    // START A TOOL
void serialEvent();                               // RS232 INTERRUPT ROUTINE
void setSrvo( char chr), setSrvoSu();                    // SET SERVO VALUES
short v8Fail();

/*F******************************************************************
*
**********************************************************************/
void 
setup()
{ 
    int   ndx;

    Serial.begin( 9600 );             // START SERIAL I/O FROM DATA TERMINAL
    Serial.print( Hdr );                        // PRINT HEADER ON DATA TERM
    Dust.begin( 9600 );               // START SFTW SERIAL I/O FROM DUST BIN
    V8.begin( 9600 );                 // START SFTW SERIAL I/O FROM DUST BIN
    pinMode( DCRUN, OUTPUT);    // I/O PIN OUTPUT FOR RUNNING DUST COLLECTOR
    Servo.begin();
    Servo.setPWMFreq( 50 );                           // SET PWM FREQ = 50HZ
    delay( 1000 );
    pinMode( MUXS2, OUTPUT );                 // DEFINE ANALOG OUTPUT TO MUX
    pinMode( MUXS1, OUTPUT );                 // DEFINE ANALOG OUTPUT TO MUX
    pinMode( MUXS0, OUTPUT );                 // DEFINE ANALOG OUTPUT TO MUX
    for( ndx =1; ndx <= NBRTOOLS; ndx++)
        BasVal[ndx] = rdSnsr( ndx ); // SAVE BASE SNSR RDGS, FOR VOLT CMPRSN
    for( ndx =1; ndx <= NBRVALVES; ndx++)
        clsVlv( ndx, 0 );                                // CLOSE ALL VALVES
}
/*F******************************************************************
*
**********************************************************************/
void 
loop()
{
    if( Rs232 == true )
        doChrs();           // CHAR ARRIVED ON THE SERIAL I/O FROM DATA TERM
    if( millis() > DustTim )
          chkDustBin();                                // CHECK DUST BIN DEPTH
    if( Mode == DUSTBINHALT )
        return;                   // DUST BIN FULL, ONLY CANCEL WILL RESTART
    chkTools();                        // SEE IF ANY TOOL'S MOTOR IS RUNNING
    if( (Mode == DSPRDG) )
        dspIRdg( 9 );                  // PASS CHAR TO DISPLAY ISNSR VOLTAGE
    if( (Mode == SETSRVO) )
        setSrvo( 9 );                           // PASS CHAR TO ADJUST SERVO
}
/*F******************************************************************
* DUSTBIN DEPTH MESSAGE PROCESSING, AFTER DUSTAVGCNT 
**********************************************************************/
void 
chkDustBin()
{
    int      dust;

    if((Mode == DSPRDG) || (Mode == SETSRVO))
        return;
    if( (dust = getDust()) < 0 )
        return( dustHalt( 3, 0, "    No Dust "));
    if( (DustAcc > 0) && (DustCnt >= DUSTAVGCNT) )
    {                           // AVERAGE DUST DEPTH REPORTS FROM DETECTOR
        DustSpc = DustAcc / DustCnt;                   // UPDATE DUST SPACE
        dspDust( DustSpc );
        DustCnt = DustAcc = 0;               // RESET ACCUMULATOR AND COUNT
        if( (DustSpc <= DUSTMIN) && (DustSpc > 0) && (DcOnFlg == true) )
        {
            if( (Mode == DUSTBINHALT) || (++DustBads < DUSTBADS) )
                return;      // ALREADY HALT MODE, OR NOT ENOUGH BAD DEPTHS
            dustHalt( 0, 3, "Dust Bin Halt");  // DC RUNING & DUST BIN FULL
        }
        else
            DustBads = 0;          // ONE GOOD DUST READING RESETS DUSTBADS
    }
}
/*F******************************************************************
* CHECK FOR TOOL MOTOR RUNNING
**********************************************************************/
void 
chkTools()
{
    int     ndx, tool =0;
    ulong   now;

    if( (Mode != AUTO) && (Mode != IDLE) )
        return;            // DO NOT CHECK FOR I FLOW UNLESS IN AUTO OR IDLE
    now = millis();
    for( ndx =1; ndx <= NBRTOOLS; ndx++)
    {                   // SCAN THROUGH TOOLS & CHECK FOR INCREASED CUR FLOW
        if( chkAmpChg( ndx ))
        {                                                         // TOOL ON
            tool = ndx;
            Mode = AUTO;
            if( tool == MITERSAW )
                AutoTime = millis() + AUTODLY;           // MITERSAW RUNNING
            break;                                              // FOUND ONE
        }
    }
    if( (ActvTool == MITERSAW) && (now < AutoTime) )
        return;                    // TOOL STARTED, WAIT AUTODLY B4 CHANGING
    if( ActvTool == tool )
        return;                                   // SAME TOOL STILL RUNNING
    if( ActvTool && !tool )
        cancel( 0 );                          // NO ACTIVE TOOL AND DC IS ON
    else if( (tool != 0) && (tool != ActvTool) )
        runTool( tool );                                       // START TOOL 
}
/*F******************************************************************
* PROCESS KBD CHRS
* 0 CANCEL ALL
* 1-8 OPEN VALVE N, BLOWER ON
* A     CYCLE ALL VALVES
* #n    TEST ISNSRS
* *n    SET SERVOS
**********************************************************************/
void
doChrs()
{
    char    chr;

    while( (chr = getChr()) != 0 )       // READ A CHAR FROM InStr, IF AVAIL
    {
        Rs232 = false;                              // CLEAR SERIAL I/O FLAG
        if( (Mode == DSPRDG) && (chr != '0'))
            return( dspIRdg( chr ) );     // PASS CHR TO DSPLY ISENSOR VOLTS
        if( (Mode == SETSRVO) && (chr != '0'))
            return( setSrvo( chr ));              // PASS CHR TO SERVO SETUP
        if( (Mode == MULTVLV) && (chr != '0'))
            return( multVlv( chr ));        // PASS CHR TO MULTI VALVE SETUP
        switch( chr )
        {
            case   '0':                          // CANCEL CURRENT OPERATION
                cancel( 0 );
                break;
            case   'A':                                  // CYCLE ALL VALVES
                cycVlvs();
                break;
            case   'B':
                multVlvSu();                         // OPEN MULTIPLE VALVES
                break;
            case '1' ... '8':            // MANUAL MODE, OPEN VALVE & RUN DC
                Mode = MANUAL;
                runTool( chr );
                break;
            case  '*':             // SPLAT, DISPLAY ISENSOR VOLTAGE READING
                dspIRdgSu();
                break;
            case  '#':                               // CRUNCH, SETUP SERVOS
                setSrvoSu();
                break;
            case  NL:                                         // GOT NEWLINE
                break;                                    // DISCARD NEWLINE
            default:
                Mode = IDLE;
                break;
        }
    }
}
/*F******************************************************************
* SETUP FOR DISPLAY ISNSR VOLTAGE READINGS, CALLED FROM doChrs()
**********************************************************************/
void
dspIRdgSu()
{
    if( DcOnFlg )
        dcOff();                                   // DC IS RUNNING, STOP IT
    Mode = DSPRDG;                               // SET DISPLAY READING MODE
    AutoTime =0;
    ActVlv = 0;
    VlvOc = CLOS;
    clrScrn();
    posCur( 0, 2 );
    Serial.print("Read Sensor");
    posCur( 1, 0 );
    Serial.print("Enter Tool Number");
}
/*F******************************************************************
* SETUP SERVO SETUP
**********************************************************************/
void
setSrvoSu()
{
    if( DcOnFlg )
        dcOff();    
    Mode = SETSRVO;
    clrScrn();
    delay( 200 ); 
    posCur( 0, 0 );
    Serial.print("    SERVO SETUP");
    posCur( 1, 0 );
    Serial.print("Enter Servo Number");
}
/*F******************************************************************
* RUN A MACHINE
**********************************************************************/
void
runTool( char chr )
{
    byte   tool;

    clrScrn();
    tool = (chr & 15);                    // STRIP OFF UPPER BITS OF CHR 0X3n
    posCur( 0, 6 );
    Serial.println( (String)ToolNam[tool] );       // ROW: 0, PRINT TOOL NAME
    if( (ActvTool > 0) && (ActvTool != tool))
    {                                                      // NEW ACTIVE TOOL
        if( DcOnFlg )
            dcOff();
        clsVlv( ActvTool, 1 );
    }
    ActvTool = tool;
    opnVlv( ActvTool );
    dcOn();
}
/*F******************************************************************
* CANCEL CURRENT OPERATION
**********************************************************************/
void
cancel( byte noPnt )
{
    short  ndx;

    if( !noPnt )
    {
        clrScrn();
        Serial.println("  Cancel Operation");
    }
    if( DcOnFlg )
        dcOff();                                         // DC IS ON, STOP IT
    for( ndx = 1; ndx <= NBRVALVES; ndx++ )
        clsVlv( ndx, 1 );                                 // CLOSE ALL VALVES
    Mode = IDLE;
    AutoTime =0;                      // CANCEL ANY TOOL RUNNING IN AUTO MODE
    RdgTool = ActvTool = 0;
    if( noPnt )
        return;
    clrScrn();
    Serial.print((String)Hdr );
}
/*F******************************************************************
* DISPLAY ISNSR VOLTAGE READINGS, CALLED FROM loop(), or doChrs()
**********************************************************************/
void
dspIRdg( char chr )
{
    short     tool, val;
    int32_t   now;
    
    if( (chr >= '1' ) && (chr <= '8' ))
    {                                                        // SET NEW TOOL
        RdgTool = (chr & 0X0F);                            // STRIP OFF 0X3n
        posCur( 1, 0 );                                      // CLEARS ROW 1
        Serial.print( (String)ToolNam[RdgTool] + " = " );
        NxtDsp = 0;                               // FORCE IMMEDIATE DISPLAY
    }
    now = millis();                                      // GET CURRENT TIME
    if((now > NxtDsp) && (RdgTool != 0))
    {                                   // TIME TO DISPLAY A CURRENT READING
        NxtDsp = (now + DSPDLY);             // SAVE TIME OF CURRENT DISPLAY
        val = rdSnsr( RdgTool );
        posCur( 1, 10 );                                    // ROW 1, COL 10
        Serial.println( val );                              // 4.88MV / INCR
    }
}
/*F******************************************************************
* CYCLE ALL VALVES TO REMOVE ANY LARGE SPLINTERS ETC.
**********************************************************************/
void
cycVlvs()
{
    char  tool, chr, buf[24];
    
    clrScrn();
    Serial.println("  Cycling Valves ");
    if( DcOnFlg )
        dcOff();    
    for( tool = 1; tool <= NBRVALVES; tool++ )
    {                                                      // FLAP ALL VALVES
        posCur( 1, 0 );                                       // ROW 2, COL 0
        Serial.println((String)ToolNam[tool] );
        opnVlv( tool );
        delay( SRVODLY );                       // WAIT FOR THE SERVO TO ACT
        clsVlv( tool, 1 );
        delay( SRVODLY );                       // WAIT FOR THE SERVO TO ACT
    }
    ActvTool = 0;
    clrScrn();
    Serial.print( Hdr );
}
/*F******************************************************************
* SERVO SETUP
**********************************************************************/
void
setSrvo( char chr )
{
    short     tool, val, rtn;
    int32_t   now;
    
    if( !ActVlv && (chr == 9))
        return;                         // CALLED FROM LOOP, NO ACTIVE VALVE
    if( !ActVlv && ((chr >= '1') && (chr <='8')))
    {                                                      // NEW SERVO NMBR
        ActVlv = chr & 15;                       // SET NEW ACTIVE VALVE NBR
          posCur( 1, 0 );                                      // CLEARS ROW 1
        Serial.print( (String)ToolNam[ActVlv] + "-" );   // LIKE "TableSaw-"
        NxtDsp = 0;                               // FORCE IMMEDIATE DISPLAY
    }
    else if( (chr >= '1') && (chr <= '8') )
    {
        switch( chr )
        {
            case '2':                                              // VAL UP
                VlvVals[ActVlv][VlvOc] += SRVOBUMP;
                break;
            case '4':                                           // OPEN VALVE
                VlvOc = OPN;         // VlvOc: VALVE SHOULD BE OPEN OR CLOSED
                break;
            case '6':                                          // CLOSE VALVE
                VlvOc = CLOS;        // VlvOc: VALVE SHOULD BE OPEN OR CLOSED
                break;
            case '8':                                               // VAL DN
                VlvVals[ActVlv][VlvOc] -= SRVOBUMP;
                break;
            default:
                break;
        }
        if( (ActVlv == 8) && (VlvOc == OPN) && ((rtn =reqV8( 'O' )) <0))
            v8Fail();
        else if( (ActVlv == 8) && (VlvOc== CLOS) && ((rtn =reqV8( 'C' )) <0))
            v8Fail();
        else
        {
            Servo.setPWM( ActVlv, 0, VlvVals[ActVlv][VlvOc]);    // SET SERVO
            delay( SRVODLY );                        // WAIT FOR SERVO TO ACT
        }
        NxtDsp = 0;                               // FORCE IMMEDIATE DISPLAY
    }
    now = millis();                                      // GET CURRENT TIME
    if((now > NxtDsp) && (ActVlv != 0))
    {
        NxtDsp = (now + DSPDLY);                // SAVE TIME OF NEXT DISPLAY
        posCur( 1, 10 );                                    // ROW 1, COL 10
        val = VlvVals[ActVlv][VlvOc];
        Serial.println( (String)VlvPos[VlvOc] + ":" +val ); // "Close: 450"
    }
}
/*F******************************************************************
* TURN DUST COLLECTOR ON
**********************************************************************/
void 
dcOn()
{
    if( Mode == DUSTBINHALT )
        return;
    digitalWrite( DCRUN, 1);
    posCur( 1, 13 );
    Serial.println("DC On ");
    DcOnFlg = true;
    DustCnt = DustAcc = 0;                         // RESET DUST ACCUM & CNT
    delay( SPINUP );
}
/*F******************************************************************
* TURN DUST COLLECTOR OFF
**********************************************************************/
void 
dcOff()
{
    posCur( 1, 3 );
    Serial.println("Stopping DC ");
    delay( 2000 );                           // WAIT FOR PLENUM/HOSE TO EMPTY
    digitalWrite( DCRUN, 0);
    delay( SPINDOWN );                                // WAIT FOR DC SPINDOWN
    posCur( 1, 0 );                             // ERASE ROW 1 TO END OF LINE
    DcOnFlg = false;
}
/*F******************************************************************
*  SAMPLING CURRENT SENSOR 1-7  (LO NIBBLE 0X0001 - 0X0111), SNSR 8 =0
**********************************************************************/
int
rdSnsr( short snsr )  // SNSR: 1 - 8
{
    int    rdVal, cnt, pin, msk =1, rtn;
    ulong   end, tmp;

    // S0:D7(LSB), S1:D6, S2:D5(MSB)  pin scans 7 - 5
    for( pin = MUXS0, msk = 1; pin > 4; --pin, msk <<=1 )
    {       // SET SENSOR # INTO MUX SEL0-SEL2 INPUTS, LSB to MSB, SNSR 8 = 0
        if( msk & snsr )    
            digitalWrite( pin, HIGH );        // WT HI PINS MUXS0,MUXS1,MUXS2
        else
            digitalWrite( pin, LOW );        // WT LOW PINS MUXS0,MUXS1,MUXS2
    }
    delay( 2 );                               // WAIT 2MS FOR SEL TO INP TIME
    end = millis() + SNSRDLY;              // SAVE CURRENT TIME + SAMPLE TIME
    for( cnt = tmp =0; millis() < end; cnt++ )
    {                                // NOISY SIGNAL, AVERAGE FOR SAMPLE TIME
        rdVal = analogRead( ZIN );                      // 0-1023 DIGITAL RDG
        tmp += rdVal;
    } // 175 - 180 samples IN 20 MS
    rtn = tmp / cnt;
    return( rtn );
}
/*F******************************************************************
* CHECK FOR AMPERAGE CHANGE ON A TOOL
**********************************************************************/
bool
chkAmpChg( int tool)
{
    int     rdg;

    rdg = rdSnsr( tool );       // READINGS RUN FROM OFF: 52-81, ON: 364-461
    if( rdg > RDGTHLD )  // NEW SENSOR IGNORE BASE (BasVal[tool] + RDGTHLD))
        return( true );                                    // RETURN TOOL ON
    else
        return( false );                                  // RETURN TOOL OFF
}
/*F******************************************************************
* GET CHAR FROM INSTR
**********************************************************************/
char
getChr()
{
    char   chr;

    if( InNdx == 0 )
        return( 0 );
    chr = InStr[--InNdx];
    ssL( InStr, InNdx );                                 // SHIFT INSTR LEFT
    if( InNdx == 0 )
        Rs232 = false;                                        // INSTR EMPTY
    return( chr );
}
/*F******************************************************************
* CLOSE A VALVE
**********************************************************************/
short 
clsVlv( uint8_t num,  byte prnt )
{
    short  rtn = 1;

    if( DcOnFlg )
        dcOff();
    VlvOc = CLOS;                       // SET CURRENT VALVE POSTION = CLOSED
    if( prnt )
    {
        posCur( 1, 0 );
        Serial.println((String)"Vlv "+num+" Clos" );
    }
    if( num == 8 )
        rtn = reqV8( 'C' );                          // REQUEST VALVE 8 CLOSE
    else
    {
        Servo.setPWM( num, 0, VlvVals[num][CLOS]);             // CLOSE VALVE
        delay( SRVODLY );                            // WAIT FOR SERVO TO ACT
    }
    Servo.setPWM( 15, 0, 400 );               // DISTRACT PCA9685 TO SERVO 15
    ActVlv = 0;
    if( prnt )
        posCur( 1, 0 );                                        // CLEAR ROW 1
}
/*F******************************************************************
* OPEN A VALVE
**********************************************************************/
short 
opnVlv( uint8_t num )
{
    int     rtn = 1;

    if( DcOnFlg )
        dcOff();
    VlvOc = OPN;
    posCur( 1, 0 );
    Serial.println((String)"Vlv "+num+" Opn");
    if( num == 8 )
        rtn = reqV8( 'O' );                           // REQUEST VALVE 8 OPEN
    else
    {
        Servo.setPWM( num, 0, VlvVals[num][OPN]);               // OPEN VALVE
        delay( SRVODLY );
    }
    ActVlv = num;
}
/*F******************************************************************
* RS-232 INTERRUPT SERVICE ROUTINE, DO NOT TARRY HERE
**********************************************************************/
void
serialEvent()
{                                         // RUNS EACH TIME (AFTER) LOOP RUNS
    int    val;
    char   chr;

    while( Serial.available()) // INTERRUPT SERVICE ROUTINE, DON'T TARRY HERE
    {
        chr = (char)Serial.read();                            // GET NEW CHAR
        if( chr < 32 )
            continue;                           // DISCARD NON_PRINtable CHAR
        if( InNdx >= INSTRMAX )
        {                                   // IN STR NDX TOO LARGE, RESET IT
            Serial.println("Overrun");
            break;
        }
        val = chr & 15;
        InStr[InNdx++] = chr;                      // ADD NEW CHAR TO InStr[]
        Rs232 = true;      // SET FLAG SO MAIN LOOP CAN DO SOMETHING ABOUT IT
    }
}
/*F******************************************************************
* REQUEST DEPTH FROM DUST DETECTOR, WAIT FOR RESPONSE
* DATA FROM THIS MAKES SERIOUS DECISIONS ABOUT SHUTING DOWN DC
**********************************************************************/
int
getDust()
{             // @ 9600 CHARS ARRIVE EVERY 1Ms
    int    val, dstVal;
    char   chr;
    ulong  end;

// Serial.println((String)"getDust: Bgn");
    Dust.listen();
    Dust.print( "D\n" );             // SEND DUST DEPTH REQUEST TO DUST DET
    DustSpc = 0;
    DustTim = millis() + DUSTCHK;     // SET TIME OF NEXT DUST DEPTH REQUEST
    val = dstVal =0;     // BELOW: < DustTim IN CASE DUSTDET DOESN'T RESPOND
    end = millis() + DUSTWAT;              // SET TIMER FOR DUST DET RESONSE
    while( millis() < end )
    {                              // LOOP HERE WHILE DUSTREQ & NOT DUSTTIME
        if( Dust.available() )
        {                                   // KEEP READING TIL FIND NEWLINE
            chr = Dust.read();                               // GET NEW CHAR
// Serial.println((String)"GotChr: "+chr);
            if( chr == '\n' )
            {                                         // END OF TRANSMISSION
                DustAcc += dstVal;                  // ACCUMULATE DUST DEPTH
                DustCnt++;                      // BUMP DUST READING COUNTER
                break;
            }
            if( (chr < '0') || (chr > '9')) 
                continue;                        // DISCARD NON NUMERIC CHAR
            val = (chr & 15); // CONVERT ASCII TO BIN: '2' (0X32) & 0X0F = 2
            dstVal = (dstVal * 10) + val;       // ACCUM DEC DUST DEPTH VALS
        }
    }
// Serial.println((String)"getDust: End, dstVal: "+dstVal);
    if( millis() > end )  
        return( -1 );
    return( dstVal );
}
/*F******************************************************************
* DUST HALT
**********************************************************************/
void 
dustHalt( byte row, byte col, char *msg)
{
    // DC RUNING & DUST BIN FULL, STOP EVERYTHING
    Mode = DUSTBINHALT;                       // ADVERTISE WHY WE'RE SHUTDOWN
    clrScrn();
    posCur( row, col );                                 // SET CURSOR POSTION
    Serial.println( (String)msg );
    cancel( 1 );                                           // STOP EVERYTHING
}
/*F******************************************************************
* OPEN MULTIPLE VALVE SETUP
**********************************************************************/
void
multVlvSu()
{
    if( DcOnFlg )
        dcOff();    
    Mode = MULTVLV;
    clrScrn();
    delay( 200 ); 
//    posCur( 0, 0 );
//    Serial.print("OPEN ANOTHER VALVE");
    posCur( 1, 0 );
    Serial.print("Enter Valve Number");
    dcOff();
}
/*F******************************************************************
*     OPEN MULTIPLE VALVES 
**********************************************************************/
void 
multVlv( char chr )                                 // OPEN MULTIPLE VALVES
{
    short   tool;

    if( (chr >= '1' ) && (chr <= '8' ))
    {                                                        // SET NEW TOOL
        Mode = MANUAL;
        tool = (chr & 0X0F);                            // STRIP OFF 0X3n
        posCur( 1, 0 );                                      // CLEARS ROW 1
        Serial.print( (String)"+ "+ToolNam[tool] );
        opnVlv( tool );
        dcOn();
    }
    else
        cancel( 0 );
}
/*F******************************************************************
* POSITION CURSOR Esc[r;ccf   SET CURSOR TO ROW COL
**********************************************************************/
void 
posCur( byte row, byte col )
{
    char    str[8];

    strcpy( str, CurPos );
    sprintf( &str[2], "%1d;%02df", row, col ); 
    Serial.print( str );
    delayMicroseconds( 200 );
}
/*F******************************************************************
* CLEAR SCREEN
**********************************************************************/
void 
clrScrn()
{
    Serial.print( ClrScrn );
    delay( 200 ); 
    DstOnScrn = false;
}
/*F******************************************************************
* RUN A MACHINE
**********************************************************************/
void
dspDust( int dustSpc )
{
    char  buf[32];

    if( !DstOnScrn )
    {                                   // NEED TO REPRINT "Dust Space"
        posCur( 3, 0 );                           // SET CURSOR POSTION
        Serial.print( (String)"Dust Space: ");
        DstOnScrn = true;
    }
    posCur( 3, 12 );
    sprintf( buf, "%02u\"", dustSpc );
    Serial.print( (String)buf );
}
/*F******************************************************************
* SEND PLANER VALVE (V8) OPEN/CLOSE REQUEST
 RETURN: 1 = OK, else -1
**********************************************************************/
short
reqV8( char req )                              // REQ IS UC LETTER 'O' or 'C'
{
    ulong  end;
    char   chr, resp =0;
    short  rtn = 1;

    V8.listen();
    V8.println((String)req );                    // SEND PLANER VALVE REQUEST
    end = millis() + V8WAT;             // SET TIMER FOR PLANER VALVE RESONSE
    while( millis() < end )
    {                                   // LOOP HERE WHILE PLANER VALVE MOVES
        if( V8.available() )
        {                                    // KEEP READING TIL FIND NEWLINE
            chr = V8.read();              // GET RESPONSE: 'O' =OK, 'F' =FAIL
            if( chr == NL )
                return( rtn );                             // END OF RESPONSE
            if( (chr != 'O') && (chr != 'K') )
                rtn = -1;
        }
    }
    return( -1 );                                    // TIMEOUT
}
/*F******************************************************************
* 
**********************************************************************/
short
v8Fail()
{
    posCur( 2, 0 );
    Serial.println((String)"V 8 Failed");
    cancel( 0 );
    return( -1 );
}