Dust Collector Automation Sketch


System Board

Data Terminal

4" Updraft Valve

4" Slider Valve




This system uses the Arduino Data Terminal for displaying mode, tool being used, dust bin free space, and controlling the system.   The 4" and 6" updraft valves make this system easy.   Of the eight tools, only 6 use current sensors, the other 2 are run using manual mode.      

The program Controls
DefaultAutomatic Operation: The system polls I-sensors for each tool and if one increases (motor turned on) it opens the associated valve, and turns on the DC's blower. The system continues monitoring the current of the tool and if it's current returns to normal (motor off), the DC is shutdown and the tool's valve is closed.
'1'-'8'Manual operation: By entering a single digit 1-8, one of the tools is manually selected, the valve closed, and the DC started.   If one of the tools is running in manual mode, and you enter another single digit 1-8 the DC shuts down, the valve is closed, the valve for the new machine is opened and the DC started again.  
'0'
(zero)
Cancel any current operation, shuts down DC, close all valves.  
'A'Cycle All Vavles Cycle all valves (1-8) to clear any debris.  
'B'Open A SECOND Valve (Manual Mode) Request a tool number, then stop DC, open that tool's valve, restart the DC.
'*' (splat)Display Sensor Reading Requests a sensor number (1-8), when it is entered the sensor is selected and it's analog reading is displayed.   Allows you to trurn on or off a machine and see it's Isnsr readings.
'#' (crunch)Set Servo Values Requests a valve number (1-8) then the valve is opened and the servo's digital value is displayed.   Entering a '2' increases the digital setting, an '8' decreases the setting.   Entering a '6' closes the valve and displays the servo's valve closed setting, again '2' and '8' increase and decrease the setting.   Entering a '4' opens the valve and displays its servo setting. Sensors are polled when not in manual mode or one of the test modes, if one reads an analog reading equal or greater than RDGTHLD, the tools is activated, valve opened, and DC turned on.   An analog read may return 1023 digital, with 5 volts max and a digital value of 0 - 1023 that means 5 / 1023 = .00488 mv / increment.   RDGTHLD = 200, or 200 * .00488 = .976 volts at the sensor.  

If I decide I need more commands, I'll add a 'Test' mode (maybe letter 'B') which will have a menu of all test requests: cycle all valves, set servo open/close, display Isnsr readings, etc.   The test mode is terminated by '0' (cancel) and while in 'Test' mode disable auto Isnsr scanning.    

One thing to note about programming, I've not done a lot of Arduino programming but I have done a lot of 'C' code programming (I learned at Bell Labs many moons ago) so the sketches may not look quite like normal Arduino programming.   I'm not a big fan of the Arduino IDE editor, so I usually use my old friend, the ubiquitous vi, bounce the IDE, compile, and download.    

Please note: CODING STYLE (Read this, it'll make the code easier to understand)  

RS-232 Communication:
I am using RS-232 to communicate to the Data Terminal, Dust Detector, and Valve 8.   The Data Terminal will be on the hardware Serial, the other two will uses the Software Serial library.   Software Serial has a requirement that you can only listen to one serial port at a time and you must explicitly state which to listen to at any given time.   In order to make this work I am using a query/response scenario, the control system wil send a request then wait for a response from that remote.   That way I know which remote to listen to for response.   These query/response actions will each have a timeout associated, so a remote having a problem won't hang up the entire control system.  

After getting the basic system up and running, I converted the original dust detector to work with it (DustDetector2).   The DustAuto System requests a dust depth from dust detector2 each second.  
Originally,
DustDetector2 probes the dust in the bin, then transmitts (RS232 Ascii) the depth, in inches, to the DustAuto System.   The Dust Collector System updates it's display of the current dust depth on the data terminal.   Upon receiving the dust depth, DustAuto averages the last 5 readings before displaying or making a decision.   If there have been 3 consecutive readings with the dust in the bin less than 8" from the top (where the sensor is) the system goes into "Dust Halt" mode and shuts down the dust collector blower motor.  
Now
The dust detector now uses an IR (Infra Red) device to dermine if the dust in it's bin is too high, base on a pot in the IR detector..   When queried by DustAuto the detector reports OK or FULL. A cancel will restore the system to IDLE state and resume probing Isensors.  
Averaging and requiring multiple sequential bad readings before halting, is due to the DustDetector looking down into a virtual tornado (chaos) in the dust bin while the system is running.  
If DustAuto doesn't receive a dust report within 30Ms, it shuts down ("Dust Halt").    

The Sketch

/***********************************************************************
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 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, TURN DC 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 );
}