Treehouse Mbed Team / Mbed 2 deprecated APS_DCM1SL2

Dependencies:   mbed

Revision:
0:44a3005d4f20
Child:
1:9f8583ba2431
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/command.cpp	Sat Nov 24 18:22:31 2018 +0000
@@ -0,0 +1,1868 @@
+//-------------------------------------------------------------------------------
+// 
+//  Treehouse Inc.
+//  Colorado Springs, Colorado
+// 
+//  Copyright (c) 2016 by Treehouse Designs Inc. 
+// 
+//  This code is the property of Treehouse, Inc. (Treehouse)
+//  and may not be redistributed in any form without prior written 
+//  permission of the copyright holder, Treehouse.
+//
+//  The above copyright notice and this permission notice shall be included in
+//  all copies or substantial portions of the Software.
+// 
+//   
+//-------------------------------------------------------------------------------
+// 
+//  REVISION HISTORY:
+//  
+//   $Author: $
+//   $Rev: $
+//   $Date: $
+//   $URL: $
+// 
+//-------------------------------------------------------------------------------
+
+#include "mbed.h"
+#include "string.h"
+#include "stdio.h"
+#include "stdlib.h"
+#include "ctype.h"
+#include "serial.h"
+#include "globals.h"
+#include "math.h"
+#include "RTC.h"
+#include "parameters.h"
+#include "spi_bb.h"
+#include "selfTest.h"
+#include "dio.h"
+#include "SOFBlock.h"
+#include "flash.h"
+#include "calibrate.h"
+
+
+/************* FILE SCOPE VARIABLES ************************/
+char setvalue = FALSE;
+int endOfCommand = 0;
+int commandError = 0;
+int menuLevel = LEVEL_MAIN;
+int readback = 0;
+
+
+
+/************************************************************
+* Routine: getDelimiter
+* Input:   none
+* Output:  none
+* Description:
+* searches for a delimiter and moves the buffer location
+* to be just past it
+*
+**************************************************************/
+void getDelimiter(void)
+{
+   ++bufloc;
+   
+   while ((rxbuf[bufloc] != ' ') &&
+          (rxbuf[bufloc] != ',') &&
+          (rxbuf[bufloc] != '=') &&
+          (rxbuf[bufloc] != 0 ))
+   {
+      bufloc++;
+   }
+}
+
+
+
+/************************************************************
+* Routine: gethex
+* Input:   hex character
+* Returns: hex integer
+* Description:
+* Converts a hex character to a value
+**************************************************************/
+char gethex(char val)
+{
+    int retval;
+    switch(val)
+    {
+        case '0':
+            retval = 0;
+            break;
+        case '1':
+            retval = 1;
+            break;
+        case '2':
+            retval = 2;
+            break;
+        case '3':
+            retval = 3;
+            break;
+        case '4':
+            retval = 4;
+            break;
+        case '5':
+            retval = 5;
+            break;
+        case '6':
+            retval = 6;
+            break;
+        case '7':
+            retval = 7;
+            break;
+        case '8':
+            retval = 8;
+            break;
+        case '9':
+            retval = 9;
+            break;
+        case 'A':
+            retval = 10;
+            break;
+        case 'B':
+            retval = 11;
+            break;
+        case 'C':
+            retval = 12;
+            break;
+        case 'D':
+            retval = 13;
+            break;
+        case 'E':
+            retval = 14;
+            break;
+        case 'F':
+            retval = 15;
+            break;
+        default:
+            retval = 0;
+            break;
+
+    }
+    return retval;
+}
+
+/************************************************************
+* Routine: showfval
+* Input:   setval (GET or SET)
+*          value  (float value to display)
+* Output:  none
+* Description:
+* Sends a floating point number (value) over the serial port
+* if it is being retrieved (GET)
+*
+**************************************************************/
+void showfval(char setval,float value)
+{
+    if(!setval)
+    {
+        sprintf(stemp," %4.9f",value);
+        sendSerial(stemp);
+    }
+}
+
+/************************************************************
+* Routine: showival
+* Input:   setval (GET or SET)
+*          value  (integer value to display)
+* Output:  none
+* Description:
+* Sends an integer (value) over the serial port
+* if it is being retrieved (GET)
+*
+**************************************************************/
+void showival(char setval, int value)
+{
+    if(!setval)
+    {
+        sprintf(stemp," %i",value);
+        sendSerial(stemp);
+    }
+}
+/************************************************************
+* Routine: showcval
+* Input:   setval (GET or SET)
+*          value  (character to display)
+* Output:  none
+* Description:
+* Sends a character over the serial port
+* if it is being retrieved (GET)
+*
+**************************************************************/
+void showcval(char setval, int value)
+{
+    if(!setval)
+    {
+        sprintf(stemp," %c",(char)value);
+        sendSerial(stemp);
+    }
+}
+
+/************************************************************
+* Routine: showlval
+* Input:   setval (GET or SET)
+*          value  (integer value to display)
+* Output:  none
+* Description:
+* Sends an long (value) over the serial port
+* if it is being retrieved (GET)
+*
+**************************************************************/
+void showlval(char setval, long value)
+{
+    if(!setval)
+    {
+        sprintf(stemp," %ld",value);
+        sendSerial(stemp);
+    }
+}
+
+/************************************************************
+* Routine: showuival
+* Input:   setval (GET or SET)
+*          value  (integer value to display)
+* Output:  none
+* Description:
+* Sends an unsigned int (value) over the serial port
+* if it is being retrieved (GET)
+*
+**************************************************************/
+void showuival(char setval, unsigned int value)
+{
+    if(!setval)
+    {
+        sprintf(stemp," %u",value);
+        sendSerial(stemp);
+    }
+}
+
+/************************************************************
+* Routine: showhval
+* Input:   setval (GET or SET)
+*          value  (hex integeger value to display)
+* Output:  none
+* Description:
+* Sends an integer (value) in hex over the serial port
+* if it is being retrieved (GET)
+*
+**************************************************************/
+void showhval(char setval, int value)
+{
+    if(!setval)
+    {
+        if(serialStatus.computer)
+            sprintf(stemp," %u",(unsigned int)value);
+        else
+            sprintf(stemp," 0x%04x",value);
+        sendSerial(stemp);
+    }
+}
+
+
+/************************************************************
+* Routine: getival
+* Input:   setval (GET or SET)
+* Returns: the value if it is being SET or 0 if it is a GET
+* Description:
+* Gets an integer from the serial port connection.
+*
+**************************************************************/
+int getival(char setval)
+{
+   if (setval)
+   {
+        return atoi(&rxbuf[++bufloc]);
+   }
+
+   return 0;
+}
+
+/************************************************************
+* Routine: getcval
+* Input:   setval (GET or SET)
+* Returns: the value if it is being SET or 0 if it is a GET
+* Description:
+* Gets an character from the serial port connection.
+*
+**************************************************************/
+int getcval(char setval)
+{
+    if(setval)
+    {
+        // skip one space
+        ++bufloc;
+        // skip spaces and the equals sign
+        while((rxbuf[bufloc] == ' ') || (rxbuf[bufloc] == '='))
+            bufloc++;
+        return rxbuf[bufloc++];
+    }
+    else
+        return 0;
+
+}
+/************************************************************
+* Routine: getlval
+* Input:   setval (GET or SET)
+* Returns: the value if it is being SET or 0 if it is a GET
+* Description:
+* Gets an long from the serial port connection.
+*
+**************************************************************/
+long getlval(char setval)
+{
+    if(setval)
+        return atol(&rxbuf[++bufloc]);
+    else
+        return 0;
+
+}
+
+/************************************************************
+* Routine: getfval
+* Input:   setval (GET or SET)
+* Returns: the value if it is being SET or 0 if it is a GET
+* Description:
+* Gets an float from the serial port connection.
+*
+**************************************************************/
+float getfval(char setval)
+{
+    if(setval)
+        return atof(&rxbuf[++bufloc]);
+    else
+        return 0;
+}
+
+
+/************************************************************
+* Routine: showRangeError
+* Input:   error -- 0 = in range, 1 = out of range
+* Output:  none
+* Description:
+* Sends a floating point number (value) over the serial port
+* if it is being retrieved (GET)
+*
+**************************************************************/
+void showRangeError(int intValue, int iBadValue, float fBadValue)
+{
+   if (intValue) sprintf(stemp, "Out of Range: %d",    iBadValue);
+   else          sprintf(stemp, "Out of Range: %0.3f", fBadValue);
+
+   sendSerial(stemp);
+}
+
+/************************************************************
+* Routine: validateEntry
+* Input:   setval (GET or SET)
+*          limlo -- low limit
+*          limhi -- high limit
+*          address -- address in eeprom to use
+* Returns:  0 if entry validates and is written
+*           1 if entry fails
+* Description:
+* Gets or sets a value in eeprom at the address but only
+* if it is between the limits will it write the value to
+* eeprom
+*
+**************************************************************/
+int validateEntry(char setvalue, float limlo, float limhi, float *address)
+{
+   float val;
+
+   if (setvalue)
+   {
+      val =  getfval(SET);
+
+      if ((val >= limlo) && (val <= limhi))
+      {
+         *address = val;
+      }
+      else
+      {
+         showRangeError(0, 0, val);
+         return 0;
+      }
+   }
+   else
+   {
+      val = *address;
+      sprintf(stemp, " %4.3f", val);
+      sendSerial(stemp);
+   }
+
+   return 1;
+}
+
+
+/************************************************************
+* Routine: validateEntry
+* Input:   setval (GET or SET)
+*          limlo -- low limit
+*          limhi -- high limit
+*          address -- address in eeprom to use
+*
+* Returns:  FALSE if entry fails
+*           TRUE  if entry validates and is written
+*           
+* Description:
+* Gets or sets a value in eeprom at the address but only
+* if it is between the limits will it write the value to
+* eeprom
+*
+**************************************************************/
+int validateInt(char setvalue, int limlo, int limhi, int *address)
+{
+   float val;
+   
+   if (setvalue)
+   {
+      val = getfval(SET);
+      
+      if ((val >= limlo) && (val <= limhi))
+      {
+         *address = val;
+      }
+      else
+      {
+         showRangeError(1, val, 0);
+         return FALSE;
+      }
+   }
+   else
+   {
+      val = *address;
+      sprintf(stemp, " %4.0f", val);
+      sendSerial(stemp);
+   }
+   
+   return TRUE;
+}
+
+
+/************************************************************
+* Routine: parseCommand
+* Input:   setvalue (GET or SET), command buffer
+* Returns: none
+* Description:
+* parses a command and gets the commandstring
+**************************************************************/
+void parseCommand(char setvalue, char *commandString)
+{
+   int i, endofc;
+   char store;
+
+   // Ignore any white space and the optional ';' character before the start of
+   // the command string (any ']' character is from the last command so skip that,
+   // too)
+   while ((isspace(rxbuf[bufloc])) || (rxbuf[bufloc] == ';') || (rxbuf[bufloc] == ']'))
+   {
+      bufloc++;
+      if ((rxbuf[bufloc] == 0x0D) || (rxbuf[bufloc] == 0)) break;
+   }
+
+   if (setvalue)
+   {
+      // We need a value for SET so hitting the end is a problem
+      if ((rxbuf[bufloc] == 0) || (rxbuf[bufloc] == 0x0D))
+      {
+         commandError = 1;
+         return;
+      }
+   }
+
+   // Find the end of the command string
+   endofc = bufloc + 1;
+
+   // White space, '[' and '?' all terminate the command string
+   while ((!isspace(rxbuf[endofc])) && (rxbuf[endofc] != '[') && (rxbuf[endofc] != '?'))
+   {
+      endofc++;
+      // (As does hitting the end of rxbuf!)
+      if ((rxbuf[endofc] == 0x0D) || (rxbuf[endofc] == 0)) break;
+   }
+
+   // Save the character that marks the end of the command string
+   store = rxbuf[endofc];
+   
+   // sprintf(stemp, "store == %c\r\n", store);
+   // sendSerial(stemp);
+   
+   // Command strings ending in '?' are readbacks
+   readback = ((store == '?') ? 1 : 0);
+   
+   // Set end to null character so string can now be copied
+   rxbuf[endofc] = 0;
+   
+   // Copy the command string into commandString
+   strcpy(commandString, &rxbuf[bufloc]);
+
+   // Convert the command string to all uppercase characters
+   for (i = 0; i < strlen(commandString); i++)
+   {
+      commandString[i] = toupper(commandString[i]);
+   }
+
+   // Replace the character we clobbered in rxbuf
+   rxbuf[endofc] = store;
+   
+   // Update bufloc to the end of the command string
+   bufloc = endofc;
+}
+
+/************************************************************
+* Routine: validateChannel
+* Input:   channel
+* Returns: none
+* Description:
+* Verifies that the channel number is a valid value between 
+* 1 and MAX_BOARDS, setting commandError if the check fails
+**************************************************************/
+void validateChannel(int channel)
+{
+   if ((channel < 1) || (channel > MAX_BOARDS))
+   {
+      sprintf(stemp, " Invalid channel/board: %d (range is 1 - %d)", channel, MAX_BOARDS);
+      sendSerial(stemp);
+      commandError = 1;
+   }
+}
+
+/************************************************************
+* Routine: setPairVariable
+* Input:   *avariable,*bvariable
+* Returns: none
+* Description:
+* sets a single channel variable
+**************************************************************/
+void setPairVariable(float *avariable, float *bvariable, float limitlo, float limithi)
+{
+   if (!readback)
+   {
+      if (validateEntry(SET, limitlo, limithi, avariable))
+      {
+         *bvariable = *avariable;
+      }
+   }
+}
+
+
+/************************************************************
+* Routine: chlprMenu
+* Input:   none
+* Returns: none
+* Description:
+* Channel Pair Menu
+**************************************************************/
+void chlprMenu(void)
+{
+   int ival;
+   
+   char commandString[80] = { 0 };
+
+   commandError = 0;
+   parseCommand(GET, commandString);
+ 
+   if (!strcmp(commandString, "AMPL"))
+   {
+      if (!readback)
+      {
+         setPairVariable(&ch[chpair][registerno].a_dwell1_volts, &ch[chpair][registerno].b_dwell1_volts, MIN_VOLTAGE, MAX_VOLTAGE);
+
+         ch[chpair][registerno].a_dwell2_volts = ch[chpair][registerno].a_dwell1_volts;
+         ch[chpair][registerno].b_dwell2_volts = ch[chpair][registerno].b_dwell1_volts;
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      sprintf(stemp, " [%d, %d] peak voltage = %0.3f V", chpair, registerno, ch[chpair][registerno].a_dwell1_volts);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "DWLPHS"))
+   {
+      if (!readback)
+      {
+         // Set dwell times using the specified value
+         setPairVariable(&dwells[registerno].a_dwell1_time, &dwells[registerno].b_dwell1_time, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS);
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      sprintf(stemp, " [%d] in-phase dwell time = %0.3f ms", registerno, dwells[registerno].a_dwell1_time);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString,"DWLPHS_OV"))
+   {
+      if (!readback)
+      {
+         setPairVariable(&ch[chpair][registerno].a_dwell1_ov, &ch[chpair][registerno].b_dwell1_ov, MIN_VOLTAGE, MAX_VOLTAGE);
+
+         ch[chpair][registerno].a_dwell2_ov = ch[chpair][registerno].a_dwell1_ov;
+         ch[chpair][registerno].b_dwell2_ov = ch[chpair][registerno].b_dwell1_ov;
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      sprintf(stemp, " [%d, %d] in-phase overvoltage = %0.3f V", chpair, registerno, ch[chpair][registerno].a_dwell1_ov);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString,"DWLPHS_OV_TIME"))
+   {
+      if (!readback)
+      {
+         // Set overvoltage times using the specified value
+         setPairVariable(&dwells[registerno].a_dwell1_ov_time, &dwells[registerno].b_dwell1_ov_time, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS);
+         dwells[registerno].a_dwell2_ov_time = dwells[registerno].a_dwell1_ov_time;
+         dwells[registerno].b_dwell2_ov_time = dwells[registerno].b_dwell1_ov_time;
+        
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      sprintf(stemp, " [%d] in-phase overvoltage time = %0.3f ms", registerno, dwells[registerno].a_dwell1_ov_time);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString,"DWLDIFFL"))
+   {
+      if (!readback)
+      {
+         // Set dwell times using the specified value
+         setPairVariable(&dwells[registerno].a_dwell2_time, &dwells[registerno].b_dwell2_time, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS);
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      sprintf(stemp, " [%d] differential dwell time = %0.3f ms", registerno, dwells[registerno].a_dwell2_time);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "ENABLE"))
+   {
+      ival = getival(SET);
+      if ((ival != 0) && (ival != 1)) ival = 0;
+
+      if (!readback)
+      {
+         ch[chpair][registerno].enabled = ival;
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      sprintf(stemp, " [%d, %d] enable = %d", chpair, registerno, ch[chpair][registerno].enabled);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "DISABLE"))
+   {
+      if (!readback)
+      {
+         ch[chpair][registerno].enabled = 0;
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      sprintf(stemp, " [%d, %d] enable = %d", chpair, registerno, ch[chpair][registerno].enabled);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "DIFF"))
+   {
+      ival = getival(SET);
+      if ((ival != 0) && (ival != 1)) ival = 0;
+
+      if (!readback)
+      {
+         ch[chpair][registerno].differential_dwell = ival;
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      sprintf(stemp, " [%d, %d] differential_dwell = %d", chpair, registerno, ch[chpair][registerno].differential_dwell);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "INVERT"))
+   {
+      ival = getival(SET);
+      if ((ival != 0) && (ival != 1)) ival = 0;
+
+      if (!readback)
+      {
+         ch[chpair][registerno].inverted = ival;
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      sprintf(stemp, " [%d, %d] inverted = %d", chpair, registerno, ch[chpair][registerno].inverted);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (strcmp(commandString, ""))
+   {
+      commandError = 1;
+   }
+}
+
+
+/************************************************************
+* Routine: setSingleVariable
+* Input:   *avariable,*bvariable
+* Returns: none
+* Description:
+* sets a single channel variable
+**************************************************************/
+void setSingleVariable(float* avariable, float* bvariable, float limitlo, float limithi)
+{
+   float* variable;
+   
+   if ((chsgl == 'A') || (chsgl == 'a'))
+   {
+      variable = avariable;
+   }
+   else
+   {
+      variable = bvariable;
+   }
+
+   if (!readback)
+   {
+      validateEntry(SET, limitlo, limithi, variable);
+   }
+}
+
+
+/************************************************************
+* Routine: chlsglMenu
+* Input:   none
+* Returns: none
+* Description:
+* Channel Single Menu
+**************************************************************/
+void chlsglMenu(void)
+{
+   int channelSide;
+   float value;
+   
+   char commandString[80] = { 0 };
+
+   commandError = 0;
+   parseCommand(GET, commandString);
+
+   channelSide = (((chsgl == 'A') || (chsgl == 'a')) ? 1 : 2);
+   
+   if (!strcmp(commandString, "DWL1AMPL"))
+   {
+      if (!readback)
+      {
+         setSingleVariable(&ch[chpair][registerno].a_dwell1_volts, &ch[chpair][registerno].b_dwell1_volts, MIN_VOLTAGE, MAX_VOLTAGE);
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      value = ((channelSide == 1) ? ch[chpair][registerno].a_dwell1_volts : ch[chpair][registerno].b_dwell1_volts);
+      
+      sprintf(stemp, " [%d%c, %d] dwell 1 peak amplitude = %0.3f V", chpair, chsgl, registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "DWL1"))
+   {
+      if (!readback)
+      {
+         // Set dwell times using the specified value
+         setSingleVariable(&dwells[registerno].a_dwell1_time, &dwells[registerno].b_dwell1_time, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS);
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      value = ((channelSide == 1) ? dwells[registerno].a_dwell1_time : dwells[registerno].b_dwell1_time);
+
+      sprintf(stemp, " [%d] dwell 1 time = %0.3f ms", registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "DWL1_OV"))
+   {
+      if (!readback)
+      {
+         setSingleVariable(&ch[chpair][registerno].a_dwell1_ov, &ch[chpair][registerno].b_dwell1_ov, MIN_VOLTAGE, MAX_VOLTAGE);
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      value = ((channelSide == 1) ? ch[chpair][registerno].a_dwell1_ov : ch[chpair][registerno].b_dwell1_ov);
+      
+      sprintf(stemp, " [%d%c, %d] dwell 1 overvoltage = %0.3f V", chpair, chsgl, registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "DWL1_OV_TIME"))
+   {
+      if (!readback)
+      {
+         // Set channel 1's dwell 1 overvoltage time
+         setSingleVariable(&dwells[registerno].a_dwell1_ov_time, &dwells[registerno].b_dwell1_ov_time, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS);
+         
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      value = ((channelSide == 1) ? dwells[registerno].a_dwell1_ov_time : dwells[registerno].b_dwell1_ov_time);
+
+      sprintf(stemp, " [%d] dwell 1 overvoltage time = %0.3f ms", registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "DWL2AMPL"))
+   {
+      if (!readback)
+      {
+         setSingleVariable(&ch[chpair][registerno].a_dwell2_volts, &ch[chpair][registerno].b_dwell2_volts, MIN_VOLTAGE, MAX_VOLTAGE);
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      value = ((channelSide == 1) ? ch[chpair][registerno].a_dwell2_volts : ch[chpair][registerno].b_dwell2_volts);
+      
+      sprintf(stemp, " [%d%c, %d] dwell 2 peak amplitude = %0.3f V", chpair, chsgl, registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString,"DWL2"))
+   {
+      if (!readback)
+      {
+         // Set dwell 2 time
+         setSingleVariable(&dwells[registerno].a_dwell2_time, &dwells[registerno].b_dwell2_time, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS);
+         
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      value = ((channelSide == 1) ? dwells[registerno].a_dwell2_time : dwells[registerno].b_dwell2_time);
+
+      sprintf(stemp, " [%d] dwell 2 time = %0.3f ms", registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "DWL2_OV"))
+   {
+      if (!readback)
+      {
+         setSingleVariable(&ch[chpair][registerno].a_dwell2_ov, &ch[chpair][registerno].b_dwell2_ov, MIN_VOLTAGE, MAX_VOLTAGE);
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      value = ((channelSide == 1) ? ch[chpair][registerno].a_dwell2_ov : ch[chpair][registerno].b_dwell2_ov);
+      
+      sprintf(stemp, " [%d%c, %d] dwell 2 overvoltage = %0.3f V", chpair, chsgl, registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "DWL2_OV_TIME"))
+   {
+      if (!readback)
+      {
+         // Set dwell 2 overvoltage time
+         setSingleVariable(&dwells[registerno].a_dwell2_ov_time, &dwells[registerno].b_dwell2_ov_time, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS);
+         
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+
+      value = ((channelSide == 1) ? dwells[registerno].a_dwell2_ov_time : dwells[registerno].b_dwell2_ov_time);
+
+      sprintf(stemp, " [%d] dwell 2 overvoltage time = %0.3f ms", registerno, value);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "ENABLE"))
+   {
+      if (!readback)
+      {
+         ch[chpair][registerno].enabled = 1;
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      sprintf(stemp, " [%d, %d] enable = %d", chpair, registerno, ch[chpair][registerno].enabled);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString,"DISABLE"))
+   {
+      if (!readback)
+      {
+         ch[chpair][registerno].enabled = 0;
+
+         sprintf(stemp, " Setting");
+         sendSerial(stemp);
+      }
+      
+      sprintf(stemp, " [%d, %d] enable = %d", chpair, registerno, ch[chpair][registerno].enabled);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (strcmp(commandString, ""))
+   {
+      commandError = 1;
+   }
+}
+
+
+/************************************************************
+* Routine: testMenu
+* Input:   none
+* Returns: none
+* Description:
+* testMenu
+**************************************************************/
+void testMenu(void)
+{
+   int ival;
+   int ch, dac;
+   float fval;
+   unsigned int data;
+
+   char commandString[80] = { 0 };
+
+   commandError = 0;
+   parseCommand(GET, commandString);
+ 
+   if (!strcmp(commandString, "LEDS")) // duh do I have to tell you?
+   {
+      testLEDs();
+      return;
+   }
+   
+   if (!strcmp(commandString, "SELFTEST"))
+   {
+      if ((rxbuf[bufloc] == 0x0D) || (rxbuf[bufloc] == 0))
+      {
+         ival = DEFAULT_SELFTEST_BOARDS;
+      }
+      else
+      {
+         ival = getival(SET);
+
+         if ((ival < 1) || (ival > MAX_BOARDS))
+         {
+            sprintf(stemp, " Invalid number of channels/boards (range is 1 - %d)", MAX_BOARDS);
+            sendSerial(stemp);
+            commandError = 1;
+            return;
+         }
+      }
+      
+      selfTest(ival);
+      return;
+   }
+      
+   if (!strcmp(commandString, "HVEN")) // high voltage enable
+   {
+      ival = getival(SET);
+      hv_en = ival;
+      return;
+   }
+
+   if (!strcmp(commandString, "DAC"))
+   {
+      ch = getival(SET);
+      getDelimiter();
+      dac = getival(SET);
+      getDelimiter();
+      data = getival(SET);
+
+      validateChannel(ch);
+      if (commandError) return;
+      ch--;
+      
+      sendData((unsigned int)ch, (unsigned int)dac, data);
+      sprintf(stemp, " ch: %u dac: %u value: %u", ch + 1, dac, data);
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "DINIT"))
+   {
+      initDACs();
+      return;
+   }
+
+   if (!strcmp(commandString, "FINIT"))
+   {
+      formatParameterSector();
+      return;
+   }
+
+   if (!strcmp(commandString, "CINIT"))
+   {
+      initCalParameters();
+      sprintf(stemp, " All calibration parameters set to default values");
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "VOLTS"))
+   {
+      ch = getival(SET);
+      getDelimiter();
+      dac = getival(SET);
+      getDelimiter();
+      fval = getfval(SET);
+
+      validateChannel(ch);
+      if (commandError) return;
+      ch--;
+      
+      if (dac == 1)
+      {
+         data = (unsigned int)(cal[ch].a_scale  *  fval + cal[ch].a_offset);
+         dac  = ADC_CH1;
+      }
+      else
+      {
+         data = (unsigned int)(cal[ch].b_scale  *  fval + cal[ch].b_offset);
+         dac  = ADC_CH2;
+      }
+  
+      sendData((unsigned int)ch, (unsigned int)dac, data);
+      sprintf(stemp, " ch: %u dac: %u value: %u", ch + 1, dac, data);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "BENB")) // board enable bits
+   {
+      ival = getival(SET);
+      setBoardEnables((unsigned int)ival);
+      sprintf(stemp, " Board Enable Bits = 0x%04x\r\n", ival); 
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "BEN")) // board enable
+   {
+      ch = getival(SET);
+      getDelimiter();
+      ival = getival(SET);
+
+      validateChannel(ch);
+      if (commandError) return;
+      ch--;
+      
+      setBoardEnable(ch, (unsigned int)ival);
+      sprintf(stemp, " Board Enable %d = %d\r\n", ch + 1, ival); 
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "LD"))
+   {
+      ival = getival(SET);
+
+      if (ival)
+      {
+         tst_ld = 1;
+      }
+      else
+      {
+         tst_ld = 0;
+      }
+
+      Delay(100);
+      ival = dtst_out;
+      sprintf(stemp, " DTST_OUT = %d\r\n", ival); 
+      sendSerial(stemp);
+      return;
+   }
+
+   if (!strcmp(commandString, "SCK"))
+   {
+      ival = getival(SET);
+
+      if (ival)
+      {
+         sclk = 1;
+      }
+      else
+      {
+         sclk = 0;
+      }
+
+      ival = dtst_out;
+      sprintf(stemp, " DTST_OUT = %d\r\n", ival); 
+      sendSerial(stemp);
+      return;
+   }
+   
+   if (!strcmp(commandString, "FLASHDEMO")) // flash test
+   {
+      flashdemo();
+      return;
+   }
+
+   if (!strcmp(commandString, "HVGOOD"))
+   {
+      sprintf(stemp, " HVCMP1: %i HVCMP2: %i", (int)hv_cmptr1, (int)hv_cmptr2);
+      sendSerial(stemp);
+      return;
+   }
+
+   if (strcmp(commandString, ""))
+   {
+      commandError = 1;
+   }
+}
+
+/************************************************************
+* Routine: setDwellTime
+* Input:   dwellType (enum)
+* Returns: none
+* Description:
+* Set the specified dwell time in [registerno] to the indicated 
+* number of milliseconds
+**************************************************************/
+enum { DWELL_1_OV_TIME,  DWELL_1_TIME,  DWELL_2_OV_TIME,  DWELL_2_TIME };
+
+void setDwellTime(int dwellType)
+{
+   char channelSideChar, setSingleChannel;
+   float fValue;
+
+   if (readback)
+   {
+      if (dwellType == DWELL_1_OV_TIME)
+      {
+         sprintf(stemp, "\r\n Channel A Dwell 1 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell1_ov_time);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n Channel B Dwell 1 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell1_ov_time);
+         sendSerial(stemp);
+      }
+      else if (dwellType == DWELL_1_TIME)
+      {
+         sprintf(stemp, "\r\n Channel A Dwell 1 Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell1_time);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n Channel B Dwell 1 Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell1_time);
+         sendSerial(stemp);
+      }
+      else if (dwellType == DWELL_2_OV_TIME)
+      {
+         sprintf(stemp, "\r\n Channel A Dwell 2 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell2_ov_time);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n Channel B Dwell 2 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell2_ov_time);
+         sendSerial(stemp);
+      }
+      else if (dwellType == DWELL_2_TIME)
+      {
+         sprintf(stemp, "\r\n Channel A Dwell 2 Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell2_time);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n Channel B Dwell 2 Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell2_time);
+         sendSerial(stemp);
+      }
+   }
+   else if (running == 1)
+   {
+      sprintf(stemp, " Parameters may not be updated while running!");
+      sendSerial(stemp);
+   }
+   else 
+   {
+      // Find the next command string character
+      while (rxbuf[bufloc] == ' ' || rxbuf[bufloc] == '[' && rxbuf[bufloc] != 0x0D ){ bufloc++; }
+
+      // Is the user setting a single channel or a channel pair?
+      channelSideChar  = toupper(rxbuf[bufloc]);
+      setSingleChannel = ((channelSideChar == 'A') || (channelSideChar == 'B'));
+
+      if (setSingleChannel) 
+      {
+         chsgl = channelSideChar;
+         
+         // Skip over the channel side character
+         bufloc++;
+
+         // Find the next command string character
+         while (rxbuf[bufloc] == ' ' || rxbuf[bufloc] == ',' && rxbuf[bufloc] != 0x0D ){ bufloc++; }
+      }
+
+      // Back up because the current character should be the start of the register 
+      // number but validateInt() is going to advance one character before calling
+      // atof()
+      bufloc--;
+
+      if (!validateInt(SET, 1, MAX_REGISTERS, &registerno))
+      {
+         sprintf(stemp, " Invalid register number (1 - %d)", MAX_REGISTERS);
+         sendSerial(stemp);
+         commandError = 1;
+      }
+
+      if (!commandError)
+      {
+         // Adjust user values (1 - MAX) to 0 offset values (0 - (MAX - 1))
+         registerno--;
+
+         // Skip over the register number
+         while (isdigit(rxbuf[bufloc]) && rxbuf[bufloc] != 0x0D ){ bufloc++; }
+
+         // Skip over white space and delimiters
+         while (rxbuf[bufloc] == ' ' || rxbuf[bufloc] == ',' && rxbuf[bufloc] != 0x0D ){ bufloc++; }
+
+         // Back up because the current character should be the start of the value
+         // but validateEntry() is going to advance one character before calling
+         // atof()
+         bufloc--;
+
+         // Read and validate the dwell time value
+         if (!validateEntry(SET, MIN_DWELL_TIME_MS, MAX_DWELL_TIME_MS, &fValue))
+         {
+            sprintf(stemp, " Invalid dwell time");
+            sendSerial(stemp);
+            commandError = 1;
+         }
+      }
+
+      if (!commandError)
+      {
+         if (dwellType == DWELL_1_OV_TIME)
+         {
+            if ((!setSingleChannel) || (channelSideChar == 'A'))
+            {
+               dwells[registerno].a_dwell1_ov_time = fValue;
+               sprintf(stemp, "\r\n Channel A Dwell 1 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell1_ov_time);
+               sendSerial(stemp);
+            }
+            if ((!setSingleChannel) || (channelSideChar == 'B'))
+            {
+               dwells[registerno].b_dwell1_ov_time = fValue;
+               sprintf(stemp, "\r\n Channel B Dwell 1 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell1_ov_time);
+               sendSerial(stemp);
+            }
+         }
+         else if (dwellType == DWELL_1_TIME)
+         {
+            if ((!setSingleChannel) || (channelSideChar == 'A'))
+            {
+               dwells[registerno].a_dwell1_time = fValue;
+               sprintf(stemp, "\r\n Channel A Dwell 1 Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell1_time);
+               sendSerial(stemp);
+            }
+            if ((!setSingleChannel) || (channelSideChar == 'B'))
+            {
+               dwells[registerno].b_dwell1_time = fValue;
+               sprintf(stemp, "\r\n Channel B Dwell 1 Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell1_time);
+               sendSerial(stemp);
+            }
+         }
+         else if (dwellType == DWELL_2_OV_TIME)
+         {
+            if ((!setSingleChannel) || (channelSideChar == 'A'))
+            {
+               dwells[registerno].a_dwell2_ov_time = fValue;
+               sprintf(stemp, "\r\n Channel A Dwell 2 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell2_ov_time);
+               sendSerial(stemp);
+            }
+            if ((!setSingleChannel) || (channelSideChar == 'B'))
+            {
+               dwells[registerno].b_dwell2_ov_time = fValue;
+               sprintf(stemp, "\r\n Channel B Dwell 2 OV Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell2_ov_time);
+               sendSerial(stemp);
+            }
+         }
+         else if (dwellType == DWELL_2_TIME)
+         {
+            if ((!setSingleChannel) || (channelSideChar == 'A'))
+            {
+               dwells[registerno].a_dwell2_time = fValue;
+               sprintf(stemp, "\r\n Channel A Dwell 2 Time [%i] = %0.3f", registerno + 1, dwells[registerno].a_dwell2_time);
+               sendSerial(stemp);
+            }
+            if ((!setSingleChannel) || (channelSideChar == 'B'))
+            {
+               dwells[registerno].b_dwell2_time = fValue;
+               sprintf(stemp, "\r\n Channel B Dwell 2 Time [%i] = %0.3f", registerno + 1, dwells[registerno].b_dwell2_time);
+               sendSerial(stemp);
+            }
+         }
+      }
+   }
+}
+
+
+// Clear the specified register
+
+void clearRegister(unsigned int reg)
+{
+   int i;
+   
+   dwells[reg].a_dwell1_time    = 0;
+   dwells[reg].a_dwell2_time    = 0;
+   dwells[reg].a_dwell1_ov_time = 0;
+   dwells[reg].a_dwell2_ov_time = 0;
+   dwells[reg].b_dwell1_time    = 0;
+   dwells[reg].b_dwell2_time    = 0;
+   dwells[reg].b_dwell1_ov_time = 0;
+   dwells[reg].b_dwell2_ov_time = 0;
+   
+   for (i = 0; i < MAX_BOARDS; i++)
+   {
+      ch[i][reg].enabled            = 0;
+      ch[i][reg].installed          = 0;
+      ch[i][reg].inverted           = 0;
+      ch[i][reg].differential_dwell = 1;
+
+      ch[i][reg].a_dwell1_volts = 0;
+      ch[i][reg].a_dwell2_volts = 0;
+      ch[i][reg].a_dwell1_ov    = 0;
+      ch[i][reg].a_dwell2_ov    = 0;
+      ch[i][reg].b_dwell1_volts = 0;
+      ch[i][reg].b_dwell2_volts = 0;
+      ch[i][reg].b_dwell1_ov    = 0;
+      ch[i][reg].b_dwell2_ov    = 0;
+   }
+}
+    
+
+/************************************************************
+* Routine: doCommand
+* Input:   none
+* Returns: none
+* Description:
+* This is the start of the command string.
+**************************************************************/
+void doCommand(void)
+{
+   int channelNum, channelSide, numPoints, i, ival;
+   char channelSideChar;
+   unsigned int boardEnables;
+
+   char commandString[80] = { 0 };
+
+   bufloc = 0;
+   commandError = 0;
+
+   parseCommand(GET, commandString);
+
+   if (!strcmp(commandString, "FREQ"))
+   {
+      if (readback)
+      {
+         sprintf(stemp, " %f", frequency);
+         sendSerial(stemp);
+      }
+      else if (running == 1)
+      {
+         sprintf(stemp, " Parameters may not be updated while running!");
+         sendSerial(stemp);
+      }
+      else
+      {
+         validateEntry(SET, 1.0, 10000.0, &frequency);
+      }
+   }
+   else if (!strcmp(commandString, "ALLOFF"))
+   {
+      hv_en = 1;
+      setBoardEnables(ALLOFF);
+   }
+   else if (!strcmp(commandString, "ALLON"))
+   {
+      hv_en = 0;
+      setBoardEnables(ALLON);
+   }
+   else if (!strcmp(commandString, "DWL1_OV_TIME"))
+   {
+       setDwellTime(DWELL_1_OV_TIME);
+   }
+   else if (!strcmp(commandString, "DWL1_TIME"))
+   {
+       setDwellTime(DWELL_1_TIME);
+   }
+   else if (!strcmp(commandString, "DWL2_OV_TIME"))
+   {
+       setDwellTime(DWELL_2_OV_TIME);
+   }
+   else if (!strcmp(commandString, "DWL2_TIME"))
+   {
+       setDwellTime(DWELL_2_TIME);
+   }
+   else if (!strcmp(commandString, "RUN"))
+   {
+      // Skip over any white space and the optional '[' character
+      while ((isspace(rxbuf[bufloc])) || (rxbuf[bufloc] == '[')) bufloc++;
+      
+      if (rxbuf[bufloc] == '0')
+      {
+         stopPulser();
+         setDacsToZeroVolts();
+      }
+      else if ((rxbuf[bufloc] > '0') && (rxbuf[bufloc] < '0' + MAX_REGISTERS))
+      {
+         ival = atoi(&rxbuf[bufloc]);
+         ival--;
+         
+         if (running == 0)
+         {
+            setDacsToZeroVolts();
+            Delay(1000);
+            registerno = ival;
+            startPulser(registerno);
+         }
+         else
+         {
+            // Compare the board enable flags between registers
+            boardEnables = checkRegisterCompatibility(ival);
+            
+            // If board enable flags match, change the register set
+            if (boardEnables == 0)
+            {
+                registerno = ival;
+            }
+            else
+            {
+               sprintf(stemp, " Board enable flags do not match (0x%08x)", boardEnables);
+               sendSerial(stemp);
+            }
+         }
+      }
+      else
+      {
+         sprintf(stemp, " Invalid register number (1 - %d)", MAX_REGISTERS);
+         sendSerial(stemp);
+         commandError = 1;
+      }
+   }
+   else if (!strcmp(commandString, "STOP"))
+   {
+      stopPulser();
+      setBoardEnables(ALLOFF);
+      hv_en = OFF;
+   }
+   else if (!strcmp(commandString, "CLEAR"))
+   {
+      if (running == 1)
+      {
+         sprintf(stemp, " Parameters may not be updated while running!");
+         sendSerial(stemp);
+         commandError = 1;
+      }
+      else if (!validateInt(SET, 0, MAX_REGISTERS, &registerno))
+      {
+         sprintf(stemp, " Invalid register number (1 - %d)", MAX_REGISTERS);
+         sendSerial(stemp);
+         commandError = 1;
+      }
+      
+      if (!commandError)
+      {
+         if (registerno == 0)
+         {
+            frequency = 1000;
+            
+            clearRegister(0);
+            clearRegister(1);
+            clearRegister(2);
+            clearRegister(3);
+
+            initCalParameters();
+            
+            sprintf(stemp, " All parameters reset to default values");
+            sendSerial(stemp);
+         }
+         else
+         {
+            // Adjust user values (1 - MAX) to 0 offset values (0 - (MAX - 1))
+            registerno--;
+
+            clearRegister(registerno);
+            
+            sprintf(stemp, " [x, %d] voltages are 0", registerno);
+            sendSerial(stemp);
+         }
+      }
+   }
+   else if(!strcmp(commandString, "TEST"))
+   {
+      if (running == 1)
+      {
+         sprintf(stemp, " Parameters may not be updated while running!");
+         sendSerial(stemp);
+      }
+      else
+      {
+         testMenu();
+      }
+   }
+   else if(!strcmp(commandString, "READ"))
+   {
+      if (!validateInt(SET, 1, MAX_REGISTERS, &registerno))
+      {
+         sprintf(stemp, " Invalid register number (1 - %d)", MAX_REGISTERS);
+         sendSerial(stemp);
+         commandError = 1;
+      }
+
+      if (!commandError)
+      {
+         // Adjust user values (1 - MAX) to 0 offset values (0 - (MAX - 1))
+         registerno--;
+         
+         sprintf(stemp, "\r\n Register [x, %d]: ", registerno + 1);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n         frequency = %0.3f Hz", frequency);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n    dwell1_ov_time = %0.3f ms", dwells[registerno].a_dwell1_ov_time);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n       dwell1_time = %0.3f ms", dwells[registerno].a_dwell1_time);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n    dwell2_ov_time = %0.3f ms", dwells[registerno].a_dwell2_ov_time);
+         sendSerial(stemp);
+         sprintf(stemp, "\r\n       dwell2_time = %0.3f ms", dwells[registerno].a_dwell2_time);
+         sendSerial(stemp);
+
+         for (i = 0; i < MAX_BOARDS; i++)
+         {
+            sprintf(stemp, "\r\n\r\n Register [%d, %d]: ", i + 1, registerno + 1);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n             enabled = %d", ch[i][registerno].enabled);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n        differential = %d", ch[i][registerno].differential_dwell);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n            inverted = %d", ch[i][registerno].inverted);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n         a_dwell1_ov = %0.3f V", ch[i][registerno].a_dwell1_ov);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n      a_dwell1_volts = %0.3f V", ch[i][registerno].a_dwell1_volts);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n         a_dwell2_ov = %0.3f V", ch[i][registerno].a_dwell2_ov);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n      a_dwell2_volts = %0.3f V", ch[i][registerno].a_dwell2_volts);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n         b_dwell1_ov = %0.3f V", ch[i][registerno].b_dwell1_ov);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n      b_dwell1_volts = %0.3f V", ch[i][registerno].b_dwell1_volts);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n         b_dwell2_ov = %0.3f V", ch[i][registerno].b_dwell2_ov);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\n      b_dwell2_volts = %0.3f V", ch[i][registerno].b_dwell2_volts);
+            sendSerial(stemp);
+         }
+      }
+   }
+   else if(!strcmp(commandString, "CAL"))
+   {
+      if (running == 1)
+      {
+         sprintf(stemp, " Parameters may not be updated while running!");
+         sendSerial(stemp);
+         commandError = 1;
+      }
+      else if (!validateInt(SET, 1, MAX_BOARDS, &channelNum))
+      {
+         sprintf(stemp, " Invalid board number (1 - %d)", MAX_BOARDS);
+         sendSerial(stemp);
+         commandError = 1;
+      }
+
+      if (!commandError)
+      {
+         // Adjust user values (1 - MAX) to 0 offset values (0 - (MAX - 1))
+         channelNum--;
+         
+         // Skip over digits parsed as part of the atoi() call
+         while (isdigit(rxbuf[bufloc])) bufloc++;
+
+         // Does the user simply want to read the current cal parameters?         
+         if (rxbuf[bufloc] == '?')
+         {
+            sprintf(stemp, "\r\nChannel %dA Scale = %0.4f Offset = %0.4f", channelNum, cal[channelNum].a_scale, cal[channelNum].a_offset);
+            sendSerial(stemp);
+            sprintf(stemp, "\r\nChannel %dB Scale = %0.4f Offset = %0.4f", channelNum, cal[channelNum].b_scale, cal[channelNum].b_offset);
+            sendSerial(stemp);
+            sendCRLF();
+            return;
+         }
+
+         // First character after digits must be either 'A' or 'B'
+         channelSideChar = toupper(rxbuf[bufloc]);
+         
+         if ((channelSideChar != 'A') && (channelSideChar != 'B'))
+         {
+            sprintf(stemp, " Invalid channel (A/B)");
+            sendSerial(stemp);
+            commandError = 1;
+         }
+      }
+
+      if (!commandError)
+      {
+         // Translate 'A' to CHAN1 and 'B' to CHAN2
+         channelSide = (channelSideChar == 'A' ? 1 : 2);
+         
+         // Move past the channel side character
+         bufloc++;
+
+         // Does the user simply want to read the current cal parameters?
+         if (rxbuf[bufloc] == '?')
+         {
+            if (channelSide == 1)
+            {
+               sprintf(stemp, "\r\nChannel %dA Scale = %0.4f Offset = %0.4f", channelNum, cal[channelNum].a_scale, cal[channelNum].a_offset);
+               sendSerial(stemp);
+            }
+            else
+            {
+               sprintf(stemp, "\r\nChannel %dB Scale = %0.4f Offset = %0.4f", channelNum, cal[channelNum].b_scale, cal[channelNum].b_offset);
+               sendSerial(stemp);
+            }
+
+            sendCRLF();
+            return;
+         }
+
+         // If no point value specified, silently default to 3-point cal
+         if (!strcmp(&rxbuf[bufloc], ""))
+         {
+            numPoints = 3;
+         }
+         else if (!validateInt(SET, 3, 11, &numPoints))
+         {
+            sprintf(stemp, " Invalid number of cal points (3 - 11)");
+            sendSerial(stemp);
+            commandError = 1;
+         }
+      }
+       
+      if (!commandError)
+      {
+         sprintf(stemp, " Starting %d-point Calibration on Channel %d%c", numPoints, channelNum, channelSideChar);
+         sendSerial(stemp);
+         
+         // Reset the serial buffer for the incoming user input
+         bufloc = 0;
+         rxbuf[bufloc] = 0;
+         
+         calibrate(channelNum, channelSide, numPoints);
+      }
+   }
+   else if (!strcmp(commandString, "COMMIT"))
+   {
+      commitParametersToFlash();
+   }
+   else if (!strcmp(commandString, "CHLPR"))
+   {
+      if (readback)
+      {
+         sprintf(stemp, " [%i, %i]", chpair, registerno);
+         sendSerial(stemp);
+      }
+      else if (running == 1)
+      {
+         sprintf(stemp, " Parameters may not be updated while running!");
+         sendSerial(stemp);
+      }
+      else 
+      {
+         if (!validateInt(SET, 1, MAX_BOARDS, &chpair))
+         {
+            sprintf(stemp, "Invalid board number (1 - %d)", MAX_BOARDS);
+            sendSerial(stemp);
+            commandError = 1;
+         }
+         
+         if (!commandError)
+         {
+            getDelimiter();
+
+            if (!validateInt(SET, 1, MAX_REGISTERS, &registerno))
+            {
+               sprintf(stemp, "Invalid register number (1 - %d)", MAX_REGISTERS);
+               sendSerial(stemp);
+               commandError = 1;
+            }
+         }
+
+         if (!commandError)
+         {
+            // Adjust user values (1 - MAX) to 0 offset values (0 - (MAX - 1))
+            chpair--;
+            registerno--;
+            
+            // ignore spaces
+            while ((rxbuf[bufloc++] == ' ') && (rxbuf[bufloc] != 0x0D) && (rxbuf[bufloc] != 0) && (rxbuf[bufloc] != ';'));
+            
+            if (rxbuf[bufloc] == 0 || rxbuf[bufloc] == 0x0D)
+            {
+               commandError = 1;
+            }
+            else
+            {
+               while((rxbuf[bufloc++] != ' ') && (rxbuf[bufloc] != 0x0D) && (rxbuf[bufloc] != 0) && (rxbuf[bufloc] == ';') );
+            
+               if (rxbuf[bufloc] == 0 || rxbuf[bufloc] == 0x0D)
+               {
+                  commandError = 1;
+               }
+               else
+               {       
+                  chlprMenu();
+               }
+            }
+         }
+      }
+   }
+   else if (!strcmp(commandString, "CHLSGL"))
+   {
+      if (readback)
+      {
+         sprintf(stemp, " [%i%c, %i]", chpair, chsgl, registerno);
+         sendSerial(stemp);
+      }
+      else if (running == 1)
+      {
+         sprintf(stemp, " Parameters may not be updated while running!");
+         sendSerial(stemp);
+      }
+      else 
+      {
+         if (!validateInt(SET, 1, MAX_BOARDS, &chpair))
+         {
+            sprintf(stemp, "Invalid board number (1 - %d)", MAX_BOARDS);
+            sendSerial(stemp);
+            commandError = 1;
+         }
+
+         if (!commandError)
+         {
+            while(rxbuf[bufloc] == ' ' || rxbuf[bufloc] == '[' || isdigit(rxbuf[bufloc]) && rxbuf[bufloc] != 0x0D ){ bufloc++; }
+            
+            channelSideChar = toupper(rxbuf[bufloc]);
+            
+            if ((channelSideChar != 'A') && (channelSideChar != 'B'))
+            {
+               sprintf(stemp, "Invalid channel (A/B)");
+               sendSerial(stemp);
+               commandError = 1;
+            }
+            
+            if (!commandError)
+            {
+               chsgl = channelSideChar;
+               getDelimiter();
+
+               if (!validateInt(SET, 1,  MAX_REGISTERS, &registerno))
+               {
+                  sprintf(stemp, "Invalid register number (1 - %d)", MAX_REGISTERS);
+                  sendSerial(stemp);
+                  commandError = 1;
+               }
+            }
+            
+            if (!commandError)
+            {
+               // Adjust user values (1 - MAX) to 0 offset values (0 - (MAX - 1))
+               chpair--;
+               registerno--;
+            
+               // ignore spaces
+               while ((rxbuf[bufloc++] == ' ') && (rxbuf[bufloc] != 0x0D) && (rxbuf[bufloc] != 0) && (rxbuf[bufloc] == ';'));
+         
+               if (rxbuf[bufloc] == 0 || rxbuf[bufloc] == 0x0D)
+               {
+                  commandError = 1;
+               }
+            }
+
+            if (!commandError)
+            {
+               while ((rxbuf[bufloc++] != ' ') && (rxbuf[bufloc] != 0x0D) && (rxbuf[bufloc] != 0) && (rxbuf[bufloc] == ';'));
+            
+               if (rxbuf[bufloc] == 0 || rxbuf[bufloc] == 0x0D)
+               {
+                  commandError = 1;
+               }
+               else       
+               {
+                  chlsglMenu();
+               }
+            }
+         }
+      }
+   }
+   else 
+   { 
+      if (strcmp(commandString, ""))
+      {
+         commandError = 1;
+      }
+   }
+
+   if (commandError)
+   {
+      sendSerial(" ?");
+   }
+
+   sendCRLF();
+}
+
+/************************************************************
+* Routine: processCommand
+* Input:   none
+* Returns: none
+* Description:
+* This is the main serial communications routine.  Everything
+* starts here as soon as a command is avaiable for processing.
+**************************************************************/
+void processCommand(void) 
+{
+   if (!serialStatus.command && !serialStatus.repeat)
+   {
+      return;
+   }
+
+   doCommand(); // if not computer (i.e. terminal) you can do the command as well
+
+   bufloc = 0;
+   rxbuf[bufloc] = 0;
+   
+   serialStatus.computer = FALSE;
+   serialStatus.command  = FALSE;
+}