/****************************************************************************/
/* Copyright 2009 MBARI.                                                    */
/* MBARI Proprietary Information. All rights reserved.                      */
/****************************************************************************/
/* Written by Mike Risi for Respirometer project in 2009                    */
/* Modified by Bob Herlien for SensorNode for xFOCE, May 2013               */
/* v1.00 Modified to support boot loader, Dec 2014			    */
/* v1.01 3/12/2015 - Bigger Rx buffers, Risi bug fix for OERR		    */
/* v1.02 3/17/2015 - Interrupt priority fix, change ENTER/EXIT_CRITICAL_REGION*/
/* v1.03 3/18/2015 - Add Enter/ExitAppCriticalRegion			    */
/*		     Clear UxSTA.OERR in Rx int				    */
/*		     Change reporting of OERR				    */
/* v1.04 3/19/2015 - Remove foreground Rx Buff, make Rx/Tx buffs 1024/512 bytes*/
/*		     Change ADC convert limit to 100 ms			    */
/* v1.05 3/26/2015 - Require "+++" to enter config mode, coil to return to cm*/
/* v1.06 4/28/2015 - Hack to relax ModBus timing spec (10ms)		    */
/****************************************************************************/

#include "p24Fxxxx.h"
#include <uart.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "dig_io.h"
#include "adc.h"
#include "spi.h"
#include "flash_mem.h"
#include "periph.h"
#include "serial.h"
#include "inst_port.h"
#include "config.h"
#include "buffer.h"
#include "sys_timer.h"

#define SW_REV_HIGH	1
#define SW_REV_LOW	6


/***************************** Modbus includes ******************************/
#include "mb.h"
#include "port.h"
#include "mbport.h"
/***************************** Modbus includes ******************************/

/****************************** Modbus defines ******************************/
#define REG_INPUT_START 1000
#define REG_INPUT_NREGS 4
#define MB_TMOUT_50US	200		/* Change Modbus timeout to 10ms (200 * 50us) */


/***************************** PIC Configuration ****************************/
/* Deleted for version downloaded via bootloader			   */
#ifdef STANDALONE
/* JTAG/Code Protect/Write Protect/Clip-on Emulation mode Watchdog Timer/ICD pins select */
_CONFIG1(JTAGEN_OFF & GCP_OFF & GWRP_OFF & FWDTEN_OFF & BKBUG_OFF & ICS_PGx2 & COE_OFF) 
/* Enable CLK switch, Disable CLK monitor, OSCO or Fosc/2, 
Primary Oscillator Mode: Disabled, Internal Fast RC oscillator */
_CONFIG2(POSCMOD_XT & FCKSM_CSDCMD & OSCIOFNC_OFF & FNOSC_PRI)
/* Disable CLK switch and CLK monitor, OSCO or Fosc/2, HS oscillator, Primary oscillator */
_CONFIG3(WPEND_WPSTARTMEM & WPCFG_WPCFGEN & WPDIS_WPDIS & WPFP_WPFP2)
#endif


/****************************** Modbus variables ****************************/
static USHORT usRegInputStart = REG_INPUT_START;
static USHORT usRegInputBuf[REG_INPUT_NREGS];

static BOOL usGotModbusFrame = FALSE;
static BOOL usUsingDefaultCfg = FALSE;
static USHORT usEnterCharCntr = 0;
static USHORT mbTimeout = MB_TMOUT_50US;

static int usBlinkyDebug = TRUE;
static int usBlinkyState = FALSE;

/*************************** Modbus implementation **************************/
static char buff[256];
static ConfigData configData;
static int resetFlag = FALSE;

int miscRegRead(unsigned int address);
void menuTask();
void menuBanner();
void menuConfig();
BOOL checkMenuSeq();
void menuHelp();
int menuOpt0(char* cmd);
int menuOpt1(char* cmd);
int menuOpt2(char* cmd);
int menuOpt3(char* cmd);
int menuOpt4(char* cmd);
int menuOpt5(char* cmd);
int menuOpt6(char* cmd);
int menuOpt7(char* cmd);
int menuOpt8(char* cmd);
int menuOpt9(char* cmd);

void modBusTask()
{
    /* poll the modbus stack */
    eMBPoll(); 

    /* Here we simply count the number of poll cycles. */
    if (++usRegInputBuf[3] == 0)
	if (++usRegInputBuf[2] == 0)
	    if (++usRegInputBuf[1] == 0)
		++usRegInputBuf[0];
}

int main (void)
{
    unsigned int i = 0;

    /* Allow nested interrupts	*/
    _NSTDIS = 0;

    /* init functions */
    periphInit();
    digInit();
    serInit();
    instPortInit();
    tmrInit();
    spiInit();
    adcInit();

    /* turn off port xcvrs */
    serXcvrSet(0, 0);
    serXcvrSet(1, 0);
    serXcvrSet(2, 0);

    SRbits.IPL = 0;		/* Turn on interrupts	*/

    /* read the config struct */
    if ( cfgRead(&configData) != 0 )
    {
        /* put serial XCVR in the default state here */
        serXcvrSet(CFG_SERPORT, DEFAULT_CFG_XMIT);

        /* set the defaults */
        configData.id = DEFAULT_ID;
        configData.baud = DEFAULT_BAUD;
        configData.blinkyDebug = usBlinkyDebug;
	configData.serialNum = 0L;
	configData.configReg = 0L;
	configData.pwrAtReset = 0;

	instPwrInit(configData.pwrAtReset);
	usUsingDefaultCfg = TRUE;
        
        /* write the config to flash memory */
        cfgWrite(&configData);
    }
    else
    {
	usUsingDefaultCfg = FALSE;
    }

    instPwrInit(configData.pwrAtReset);

    /* set blinky debug mode based on config */
    usBlinkyDebug = configData.blinkyDebug;

    eMBInit(MB_RTU, (UCHAR)configData.id, 0, configData.baud, MB_PAR_NONE);

    /* Hack to relax ModBus timing spec to allow up to 10ms inter-char timing */
    xMBPortTimersInit(MB_TMOUT_50US);

    /* Enable the Modbus Protocol Stack. */
    eMBEnable();

    /* turn blinky on if in debug mode */
    if ( usBlinkyDebug )
        digSetBlinky(1);

    for(;;)
    {
        /* service various functions that need periodic polling */
        tmrTask();
        modBusTask();

        /* start polling instrument port 
        tasks once modbus messages have started */
        if ( usGotModbusFrame )
        {
            instPortTxTask();

            /* if blinky is in debug mode toggle it */
            if ( usBlinkyDebug )
            {
                if ( ++i > 1000 )
                {
                    i = 0;
                    digSetBlinky(!digGetBlinky());
                }
            }
        }
        else
        {
	    if (checkMenuSeq())
		menuTask();

            /* If the resetFlag is set return from main which will reset
            the processor */

            if ( resetFlag )
	    {
		usEnterCharCntr = 0;
                return 0;
	    }
        }
    }
}

/****************************************************************************/
/*                             Modbus callbacks                             */
/****************************************************************************/

eMBErrorCode
eMBRegInputCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    int             iRegIndex;

    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    if( ( usAddress >= REG_INPUT_START )
        && ( usAddress + usNRegs <= REG_INPUT_START + REG_INPUT_NREGS ) )
    {
        iRegIndex = ( int )( usAddress - usRegInputStart );
        while( usNRegs > 0 )
        {
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] >> 8 );
            *pucRegBuffer++ =
                ( unsigned char )( usRegInputBuf[iRegIndex] & 0xFF );
            iRegIndex++;
            usNRegs--;
        }
    }
    else
    {
        eStatus = MB_ENOREG;
    }


    return eStatus;
}

#define REG_HOLDING_MAX 0x400 

eMBErrorCode
eMBRegHoldingCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNRegs,
                 eMBRegisterMode eMode )
{
    eMBErrorCode    eStatus = MB_ENOERR;
    USHORT          loByte;
    USHORT          hiByte;
    USHORT          reg;

    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    if ( usAddress > REG_HOLDING_MAX )
        return MB_ENOREG;

    /* read mode */
    if ( eMode == MB_REG_READ )
    {
	/* read the register(s) */
	if ( ((usAddress + usNRegs) > ADC_CONVERT_START) && 
	     (usAddress <= ADC_CONVERT_END) )
        {
	    adcConvert(usAddress, usNRegs);
	}

	while( usNRegs > 0 )
	{
	    /* set the reg to zero just in case there is no representation */
	    reg = 0;

	    if ((usAddress >= MISC_START) && (usAddress <= MISC_END))
		reg = miscRegRead(usAddress);

	    if ((usAddress >= ADC_CONVERT_START) && (usAddress <= ADC_PARM_END))
		reg = adcReadReg(usAddress);

	    if ((usAddress >= INST_HOLDING_START) && (usAddress <= INST_HOLDING_END))
		reg = instPortReadReg(usAddress);

	    /* assign it to the reg buffer */
	    *pucRegBuffer++ = (unsigned char)( reg >> 8 );
	    *pucRegBuffer++ = (unsigned char)( reg & 0xFF );

	    /* increment register index and address */
	    --usNRegs;
	    ++usAddress;
	}
    }

    /* write mode */
    if ( eMode == MB_REG_WRITE )
    {
        /* grab a register */
	if ( ((usAddress + usNRegs) > ADC_HOLDING_START) && 
	     (usAddress <= ADC_HOLDING_END) )
        {
	    eStatus = adcWriteRegs(pucRegBuffer, usAddress, usNRegs);
	}

	while( usNRegs > 0 )
	{
	    /* get a register from the reg buffer */
	    hiByte = *(pucRegBuffer++);
	    hiByte <<= 8;
	    loByte = *(pucRegBuffer++);

	    reg = ((hiByte & 0xFF00) + (loByte & 0x00FF));

	    if ( (usAddress >= INST_HOLDING_START) && 
		 (usAddress <= INST_HOLDING_END) )
	    {
		instPortWriteReg(usAddress, reg);
	    }

	    if ((usAddress == MB_TIMEOUT) && (reg >= 20))
	    {
		xMBPortTimersInit(reg);
		mbTimeout = reg;
	    }

	    /* increment register index and address */
	    --usNRegs;
	    ++usAddress;
	}

	/* If a write is ending then all Tx write 
	   transactions are over, so clear all txWriteRequest registers. */
	instPortClearWriteReq();
    }

    return eStatus;
}


#define FIRST_COIL   1
#define TOTAL_COILS  13

int isBitSet(int bitNum, UCHAR* buff)
{
    return(buff[bitNum/8] & (1 << (bitNum%0x7)));
}

void bitSet(int bitNum, UCHAR* buff, int val)
{
    /* set of clear the bit depending on the val */
    if ( val ) 
        buff[(bitNum / 8)] |= (1 << (bitNum&0x7));
    else
        buff[(bitNum / 8)] &= ~(1 << (bitNum&0x7));

    return;
}

eMBErrorCode
eMBRegCoilsCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNCoils,
               eMBRegisterMode eMode )
{
    int i, coil, val;
    
    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    /* only coils 1 - 12 are represented */
    if ( (usAddress < FIRST_COIL) || 
         (usAddress + usNCoils) > (TOTAL_COILS + FIRST_COIL) )
        return(MB_ENOREG);

    /* perform a coil write(s) */
    if ( eMode == MB_REG_WRITE )
    {
        for (i = 0; i < usNCoils; ++i)
        {
            /* calculate the coil number */
            coil = i + usAddress;

            /* see if the bit is set for that coil */
            val = isBitSet(i, pucRegBuffer);

            /* take the appropriate action for that coil */
            switch ( coil )
            {
                /* instrument port power bits */
                case  1: instPortPwrSet(0, val); break;
                case  2: instPortPwrSet(1, val); break;
                case  3: instPortPwrSet(2, val); break;
                case  4: instPortPwrSet(3, val); break;
                /* instrument port xcvr bits */
                case  5: serXcvrSet(0, val); break;
                case  6: serXcvrSet(1, val); break;
                case  7: serXcvrSet(2, val); break;
	        case 8: break;
	        case 9: break;
                case 10: usBlinkyDebug = val; break;
                case 11: usBlinkyState = val; break; 
	        case 12: instSetDCalwaysOn(val ? TRUE : FALSE); break;
	        case 13: usGotModbusFrame = FALSE;
		    usEnterCharCntr = 0;
		    digSetBlinky(1);
		    break;
	        default: return(MB_ENOREG);
            }
        }

        /* set blinky based on debug state */
        if ( !usBlinkyDebug )
            digSetBlinky(usBlinkyState);

        return(MB_ENOERR);
    }


    /* perform a coil read(s) */
    if ( eMode == MB_REG_READ )
    {
        /* clear all of the coil bytes in the buffer */
        memset(pucRegBuffer, 0, ((usNCoils / 8) + 1));

        for (i = 0; i < usNCoils; ++i)
        {
            /* calculate the coil number */
            coil = i + usAddress;
            
            switch ( coil )
            {
                /* instrument port power bits */
                case  1: bitSet(i, pucRegBuffer, instPortPwrGet(0)); break;
                case  2: bitSet(i, pucRegBuffer, instPortPwrGet(1)); break;
                case  3: bitSet(i, pucRegBuffer, instPortPwrGet(2)); break;
                case  4: bitSet(i, pucRegBuffer, instPortPwrGet(3)); break;

                /* instrument port xcvr bits */
                case  5: bitSet(i, pucRegBuffer, serXcvrGet(0)); break;
                case  6: bitSet(i, pucRegBuffer, serXcvrGet(1)); break;
                case  7: bitSet(i, pucRegBuffer, serXcvrGet(2)); break;

		/* Unused, set bit to 0      */
                case 8: bitSet(i, pucRegBuffer, 0); break;
	        case 9: bitSet(i, pucRegBuffer, 0); break;

		/* Debug and ADC             */
                case 10: bitSet(i, pucRegBuffer, usBlinkyDebug); break;
                case 11: bitSet(i, pucRegBuffer, usBlinkyState); break;
	        case 12: bitSet(i, pucRegBuffer, instGetDCalwaysOn()); break;
	        case 13: break;
                default: return(MB_ENOREG);
            }
        }

        return(MB_ENOERR);
    }

    return(MB_EIO);
}

eMBErrorCode
eMBRegDiscreteCB( UCHAR * pucRegBuffer, USHORT usAddress, USHORT usNDiscrete )
{
    /* set the modbus frame received flag to true */
    usGotModbusFrame = TRUE;

    return MB_ENOREG;
}

/****************************************************************************/
/*                              Misc reg functions                          */
/****************************************************************************/
int miscRegRead(unsigned int address)
{	
    switch(address)
    {
      case SERIAL_NUM:
	  return(configData.serialNum);

      case SOFTWARE_REV:
	  return((SW_REV_HIGH << 8) | (SW_REV_LOW & 0xff));

      case CONFIG_REG_H:
	  return((configData.configReg >> 16) & 0xff);

      case CONFIG_REG_L:
	  return(configData.configReg & 0xff);

      case RELAY_STATE:
	  return(instPwrGet());

      case HW_CONFIG_BITS:
	  return((PORTB >> 8) & 0xff);

      case MB_TIMEOUT:
	  return(mbTimeout);

      default:
	  return(0);
    }

    return(0);
}


/****************************************************************************/
/*                              Menu functions                              */
/****************************************************************************/
#define MAX_CHARS   32
int getLine(char* line)
{
    static int char_count = 0;
    static char local_line[MAX_CHARS] = "";
    char terminator = '\r';
    int got_line;
    BYTE b;
    char c;
    int i;
    
    got_line = FALSE;

    if ( serGetByte(CFG_SERPORT, &b) )
    {
        /* echo the characters */
        serPutByte(CFG_SERPORT, b);
        
        /* assign byte to char */
        c = (char)b;
            
        if (c == '\r')
            serPutByte(CFG_SERPORT, (BYTE)'\n');

        if ( c != terminator )
        {
            if ( (c != '\b') && (char_count < MAX_CHARS) )
                local_line[char_count++] = c;
            else if ( char_count > 0 )
                --char_count;

            line[0] = '\0';
        } 
        else
        {
            got_line = TRUE;
            
            /* copy local_line buffer to line */
            for (i = 0; i <= char_count; i++)
                line[i] = local_line[i];
            
            /* terminate the line */
            line[char_count] = '\0';
            
            /* clear the char counter for the new line */
            char_count = 0;
        }
    }

    return got_line;
}

BOOL checkMenuSeq()
{
    BYTE b;

    if (usEnterCharCntr >= 3)
	return(TRUE);

    if (serGetByte(CFG_SERPORT, &b))
    {
        serXcvrSet(CFG_SERPORT, 1);

	if (b == '+')
	    usEnterCharCntr++;
	else
	    usEnterCharCntr = 0;
    }

    if (usEnterCharCntr >= 3)
    {
	menuBanner();
	if (usUsingDefaultCfg)
	    serPutString(CFG_SERPORT, "Failed to read config, using default config\r\n");
	menuConfig();
	menuHelp();
	return(TRUE);
    }
    return(FALSE);
}

void menuTask()
{
    BYTE b;

    if ( serGetByte(CFG_SERPORT, &b) )
    {
        switch ( b )
        {
            case '0': menuOpt0(NULL); break;
            case '1': menuOpt1(NULL); break;
	    case '2': menuOpt2(NULL); return;
            case '3': menuOpt3(NULL); break;
            case '4': menuOpt4(NULL); break;
            case '5': menuOpt5(NULL); break;
            case '6': menuOpt6(NULL); break;
            case '7': menuOpt7(NULL); break;
            case '8': menuOpt8(NULL); break;
            case '9': menuOpt9(NULL); break;
            case '?':
            case 'h':
            case 'H':
            case '+':
		menuHelp();
		break;
            case '\r': serPutString(CFG_SERPORT, "\r\n"); break;
            default: serPutString(CFG_SERPORT, "Invalid command, ");
                     serPutString(CFG_SERPORT, "try '?' for help.\r\n");
        }
    }

    return;
}

void menuBanner()
{
#ifdef STANDALONE
    sprintf(buff, "\r\nSensorNode I/O standalone version SW Rev %u.%02u %s, %s\r\n",
	    SW_REV_HIGH, SW_REV_LOW, __DATE__, __TIME__);
#else
    sprintf(buff, "\r\nSensorNode I/O boot version SW Rev %u.%02u %s, %s\r\n",
	    SW_REV_HIGH, SW_REV_LOW, __DATE__, __TIME__);
#endif
    serPutString(CFG_SERPORT, buff);
    serPutString(CFG_SERPORT, "Type ? for menu\r\n\r\n");
}

void menuConfig()
{
    sprintf(buff, "SLAVE ID:     %u\r\n", configData.id);
    serPutString(CFG_SERPORT, buff);
    sprintf(buff, "BAUD RATE:    %lu\r\n", configData.baud);
    serPutString(CFG_SERPORT, buff);

    serPutStringBool(CFG_SERPORT, "BLINKY DEBUG: ", configData.blinkyDebug);
    serPutStringBool(CFG_SERPORT, "RESET POWER:  ", configData.pwrAtReset);

    sprintf(buff, "SERIAL NUM:   %lu\r\n", configData.serialNum);
    serPutString(CFG_SERPORT, buff);
    sprintf(buff, "CONFIG REG:   %lx (hex)\r\n", configData.configReg);
    serPutString(CFG_SERPORT, buff);
    sprintf(buff, "HW CFG BITS:  %x (hex)\r\n", miscRegRead(HW_CONFIG_BITS));
    serPutString(CFG_SERPORT, buff);
    serPutString(CFG_SERPORT, "\r\n");
}

void menuHelp()
{
    menuOpt0("?");
    menuOpt1("?");                
    menuOpt2("?");
    menuOpt3("?");
    menuOpt4("?");
    menuOpt5("?");
    menuOpt6("?");
    menuOpt7("?");
    menuOpt8("?");
    menuOpt9("?");
}

int menuOpt0(char* cmd)
{
    /* process cmd */
    if ( cmd == NULL )
    {
        /* erase the old config */
        memErase();

        cfgWrite(&configData);
        
        resetFlag = TRUE;

        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        serPutString(CFG_SERPORT, "0 - Save config changes and reset\r\n");
        return 0;
    }

    return 0;
}

int menuOpt1(char* cmd)
{
    /* process cmd */
    if ( cmd == NULL )
    {
        menuBanner();
        menuConfig();

        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        serPutString(CFG_SERPORT, "1 - Show build date and config\r\n");
        return 0;
    }

    return 0;
}

int menuOpt2(char* cmd)
{
    unsigned int id = 0;
    /* process cmd */
    if ( cmd == NULL )
    {
        serPutString(CFG_SERPORT, "Enter SLAVE ID: ");
        
        /* wait for user input */
        while ( !getLine(buff) )
        {
            modBusTask();

            /* if you get a modbus message, bail out of menuOpt */
            if ( usGotModbusFrame )
                return 0;
        }

        id = (unsigned int)atoi(buff);

        if ( (id < 1) || (id > 32) )
        {
            sprintf(buff, "Ivalid ID: %u, valid range is [1,32]\r\n", id);
            serPutString(CFG_SERPORT, buff);
            return -1;
        }

        configData.id = id;
        
        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        serPutString(CFG_SERPORT, "2 - Set slave address\r\n");
        return 0;
    }

    return 0;
}

int menuOpt3(char* cmd)
{
    unsigned long baud;

    /* process cmd */
    if ( cmd == NULL )
    {
        serPutString(CFG_SERPORT, "Enter BAUD RATE: ");
        
        /* wait for user input */
        while ( !getLine(buff) )
        {
            modBusTask();
        
            /* if you get a modbus message, bail out of menuOpt */
            if ( usGotModbusFrame )
                return 0;
        }

        baud = (unsigned long)atol(buff);

        switch ( baud )
        {
            case 19200: break;
            case 38400: break;
            case 57600: break;
            case 115200: break;
            default:    sprintf(buff, "Invalid BAUD RATE: %lu\r\n", baud);
                        serPutString(CFG_SERPORT, buff);
                        serPutString(CFG_SERPORT, "Valid rates are\r\n");
                        serPutString(CFG_SERPORT, "19200\r\n");
                        serPutString(CFG_SERPORT, "38400\r\n");
                        serPutString(CFG_SERPORT, "57600\r\n");
                        serPutString(CFG_SERPORT, "115200\r\n");
                        return -1;
        }

        configData.baud = baud;

        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        serPutString(CFG_SERPORT, "3 - Set Modbus baudrate\r\n");
        return 0;
    }

    return 0;
}

int menuOpt4(char* cmd)
{
    int state = 0;

    /* process cmd */
    if ( cmd == NULL )
    {
        serPutString(CFG_SERPORT, "Enter BLINKY DEBUG [0 = off | 1 = on]: ");
        
        /* wait for user input */
        while ( !getLine(buff) )
        {
            modBusTask();

            /* if you get a modbus message, bail out of menuOpt */
            if ( usGotModbusFrame )
                return 0;
        }
        
        state = atoi(buff);

        if ( (state != 0) && (state != 1) )
        {
            sprintf(buff, "Ivalid state: %d, [0 = off | 1 = on]\r\n", state);
            serPutString(CFG_SERPORT, buff);
            return -1;
        }

        configData.blinkyDebug = state;

        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        serPutString(CFG_SERPORT, "4 - Set blinky debug state\r\n");
        return 0;
    }

    return 0;
}

int menuOpt9(char* cmd)
{
    /* Was menuOpt5, we removed CONFIG XMIT and renumbered */
    return 0;
}

int menuOpt5(char* cmd)
{
   int state = 0;

    /* process cmd */
    if ( cmd == NULL )
    {
        serPutString(CFG_SERPORT, "Enter power relays at RESET [0 = off | 1 = leave relays unchanged]: ");
        
        /* wait for user input */
        while ( !getLine(buff) )
        {
            modBusTask();

            /* if you get a modbus message, bail out of menuOpt */
            if ( usGotModbusFrame )
                return 0;
        }
        
        state = atoi(buff);

        if ( (state != 0) && (state != 1) )
        {
            sprintf(buff, "Invalid state: %d, [0 = off | 1 = on]\r\n", state);
            serPutString(CFG_SERPORT, buff);
            return -1;
        }

        configData.pwrAtReset = state;

        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        /* put help string here */
        serPutString(CFG_SERPORT, "5 - Set power reset state\r\n");
        return 0;
    }

    return 0;
}

int menuOpt6(char* cmd)
{
    /* process cmd */
    if ( cmd == NULL )
    {
        serPutString(CFG_SERPORT, "Enter Board Serial Number: ");
        
        /* wait for user input */
        while ( !getLine(buff) )
        {
            modBusTask();
        
            /* if you get a modbus message, bail out of menuOpt */
            if ( usGotModbusFrame )
                return 0;
        }

        configData.serialNum = (unsigned int)atoi(buff);

        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        serPutString(CFG_SERPORT, "6 - Set board serial number\r\n");
        return 0;
    }

    return 0;
}

int menuOpt7(char* cmd)
{
    /* process cmd */
    if ( cmd == NULL )
    {
        serPutString(CFG_SERPORT, "Enter Board configuration register in hex: ");
        
        /* wait for user input */
        while ( !getLine(buff) )
        {
            modBusTask();
        
            /* if you get a modbus message, bail out of menuOpt */
            if ( usGotModbusFrame )
                return 0;
        }

        configData.configReg = (unsigned long)strtol(buff, NULL, 16);

        return 0;
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
        serPutString(CFG_SERPORT, "7 - Set board configuration register\r\n");
        return 0;
    }

    return 0;
}

int menuOpt8(char* cmd)
{
    int state = 0;
    
    /* process cmd */
    if ( cmd == NULL )
    {
        serPutString(CFG_SERPORT, "Confirm bootloader flag set?  [0 = No | 1 = Yes]: ");
        
        /* wait for user input */
        while ( !getLine(buff) )
        {
            modBusTask();

            /* if you get a modbus message, bail out of menuOpt */
            if ( usGotModbusFrame )
                return 0;
        }
        
        state = atoi(buff);

        if ( state == 1 )
        {
            serPutString(CFG_SERPORT, "Setting bootloader flag\r\n");
            
            cfgSetBootFlag();
            resetFlag = TRUE;
            
            return 0;
        }
    }

    /* print help string */
    if ( cmd[0] == '?' )
    {
#ifndef STANDALONE
        serPutString(CFG_SERPORT, "8 - Set bootloader flag and reset\r\n");
#endif
        return 0;
    }

    return 0;
}


