/*
 * pHFET-SOS:  MBARI software for MFET, MpHOx and NanoFET boards, derived from Scripps Institute SeapHOx software.
 * Commemorating the contributions
 *   David Muller, davehmuller@gmail.com
 *   Germán Alfaro, alfaro.germanevera@gmail.com
 *   Rob Glatts, rglatts@ucsd.edu (from 4/2014)
 *   Taylor Wirth, twirth@ucsd.edu (from 4/2015)
 *
 * Thom Maughan (TM), MBARI, tm@mbari.org (from 2/2018)
 *
 * v3.1.2:  deployment for Ischia
 * v3.1.3:  interim version, decided to roll to v3.1.4 for Glider and Cleanup
 * v3.1.4:  added configuration of data fields in get_sample, added get_header (gh) command  14 Aug 2018
 * v3.1.5:  glider deployment  Nov 2018  added 'sl' command and 'ts'   sl (send last) uses uSD to cache sample.  This makes the glider code simpler.
 * v3.1.6   glider deployment with mods to workaround power glitching (if sample period is 0, and power cycled, start in deployed mode)
 * v3.1.7:  Update fatfs to fix 'debug' build crash.  8 Feb 2019, Fix in ff.c (uninitialized FatFs[0] pointer in Debug Build.  Release relies on linker init of pointer
 * v3.2.0:  Bug fixes (spurious serial wakeup, aligned to hour default, var headers data cfg), bring SEAPHOX up to the same state as MFET.  29 Apr 2019 TM
 * v3.2.1:  Interim build tag, reduction of delays to improve sample request-response time and to reduce power.  Identified eeprom writecycle (500K) bug - eeprom being written on each sample
 * v3.2.2:  Project named changed from SeaFET_SeapHOx to SeapHOx_MFET.   Using register file in hibernate module to store volatile data in config struct to prevent eeprom burn out
 * v3.3.0:  Adding support for MFETrevB/MCAPrevC  1) different rs232 xcvr (MAX2333) so EN# and SHUTD#,  2) i2c_fram.c basic driver, 3) optode (has bug in ring buffer)
 * v3.3.1:  fram support for nonvolatile data (sequence number, etc) tested on MFET v2.  Sep 2019
 * v3.8.0   removed MFET v1 support.   3.8 and on require FRAM
 * v3.9.1   Release candidate for MPHOX
 *   - trim sbe37 time to minimal [DONE]
 *  - finish pump sequencer [DONE]
 *          - single pump / legacy [DONE]
 *          - add data headers, data to sampling showing pump cycles [DONE]
 *              - PUMP1   PUMP2
 *              - 1/7     0/10
 *      - check bug list
 *          - prepend vs postpend spaces on headers / data
 *      - add pump seq config to metadata header (cycles, ontime)
 *      - finish ui for pump sequencer [DONE]
 *     - power glitch pops it to main menu with rtc sampling.   should stay sampling. [DONE]
 *      - finish experiment duty cycle [PARKED]
 *      - finish pump delay start - no sampling during pump start delay. [DONE]
 *      - finish beamsv2 state machine [PARKED]
 *      - merge irene's code [PARKED]
 *      - publish to git [PARKED]
 *      - add 128KB FRAM support [DONE].   Found a bug in 4KB FRAM (write then read first 4 128 byte chunks has errors)
 *      - Notes and Questions:
 *          - should we sample when the pump is off? No
 *          - we are waking on the sampling interval and using that to 'tick time'
 *      - first sample after pump delay shows neither pump 'on' according to cycle counts [BUG]
 *          SampNum             MM/DD/YYYY HH:MM:SS        VbatMain        VbiasPos        VbiasNeg         TC_cont    Humidity          Vtherm        VthrmStd         TC_Dfet            Vrsi        Vrsi_std        pHintEst        Vrse        Vrse_std          Vrsi_B              Vk          Vk_std              Ik              Ib    Pump1   Pump2
 *          #0000000             02/24/2022 08:41:07           11.98            2.61            0.09           14.66          45        0.504095        0.000247          45.870        0.054992        0.000004           7.551    0.001321        0.000114        0.607783        0.417651        0.000006          -61.31         219.370    P1 0/3  P2 0/5
 *          #0000001             02/24/2022 08:41:25           11.99            2.61            0.08           14.66          45        0.520470        0.000306          44.900        0.054950        0.000003           7.556    0.001317        0.000113        0.607744        0.417667        0.000007          -61.34         219.371    P1 1/3  P2 0/5
 *      v4.1.5
 *       added firmware version to sys_data struct - did not change overall size, used spares
 *       changed menu under config - added advanced.  moved qa, eeprom copy, etc. under advanced
 *       added the ability to print meta data ('m') to several levels
 *       added wirewalker databarf mode.   configure by 1 - Config, 7 - data fields,  7- wirewalker use case.  0.5 sec intervals.   Databarf will start after the configured period, 2 sec recommended.
 *       fixed offset in epoch_msec, still off by a half a minute
 * v4.2.3   Release for nanoFET (kittyFET), MFET, MPHOX
 * v5.0.0   Rework and simplify the power/sleep statemachine, add m2m comms and support menu system, remove unnecessary parms from sys_data
 */
// NOTE: // VERSION is defd in system.h


// mA-Hr * Volts * 3.6 = Joules
// pH sampling  12.0v @ 14.5mA for 1.2seconds => 0.2 Joules, sleep is 3uA

// TODO
//  uart1,2,.., etc def for nanoFET (#defines at top of *.c file for UART pins


#include "stdint.h"
#include "system.h"

#include "init.h"
#include "test.h"
#include "config.h"

#include "user_io.h"
#include "microsd.h"
#include "fatfs/src/ff.h"
#include "ads1248_iso.h"
#include "ads1248_noniso.h"
#include "sleep.h"
#include "board_util.h"
#include "sample.h"
#include "uartstdio.h"
#include "serial.h"

// TODO Force a compile error if more than one board is = 1 (BOARD_MPHOX>=1, BOARD_MFET>=1, BOARD_NANOFET>=1)

void exec_main_menu(void);
void print_main_menu(void);
void exec_main_loop(uint32_t timeout);
void deploy(deploy_state state, unsigned int wokebyserialFlg);
void help_menu(void);
void eeprom_firmware_qa_check(void);

void state_dump(void);      // diagnostic
uint32_t compute_nextWakeUp(void);
uint32_t exec_command(uint32_t timeout);

/*
 * Global System variables.
 */
extern uint32_t adc_data[];
extern char Optode_buff[];
extern float fBiasPos;  // used for read_bias_bat();
extern float fBiasNeg;
extern int microSD_flg;

extern unsigned char prnBuf[];  //defd in sample.c, size 1024 defd in system.h

struct systemData sys_data;
struct systemSamplingData sys_samp;         // sys variables updated with each sample
struct sampData sampl;   // defd in system.h


int dbg_flag = 1;                 // global var used for dbg_printf
int ads1248noniso_flg = 0;

unsigned int deployDelay = 30;      // 30 sec timeout

unsigned int databarFlg = 0;
unsigned int barfSampleTime = 5;

//unsigned int wokebyserialFlg;

int calFileFlg = 0;             // calFileFlg is used for ymodem to receive and parse a cal file without a filesystem for storage - config.c, ymodem.c

#ifdef APFTODO
uint32_t apf_errno    = 0;       //was errno
uint32_t apf_isus_spec_pwr = 0;
uint32_t apf_isus_lamp_pwr = 0;
uint32_t apf_isus_ref_pwr = 0;
uint32_t apf_dura_atod_pwr = 0;
uint32_t apf_sys_sensor_mode = 0;   //ISUS_SENSOR_MODE or DURA_SENSOR_MODE
uint32_t apf_sys_sensor_type = 0;
uint32_t apf_sys_por_ctr = 0;
uint32_t apf_sys_reset_ctr = 0;
uint32_t apf_sys_reset_time = 0;
uint32_t apf_isus_error_time = 0;
uint32_t apf_dura_error_time = 0;
uint32_t apf_isus_sample_time = 0;
uint32_t apf_dura_sample_time = 0;
uint32_t apf_gotSample = 0;      // flag for indicating sample taken
uint32_t apf_acquire_time = 0;   // time to acquire sample
uint32_t apf_dura_error_ctr = 0;
//uint32_t apf_dura_error_time = 0;
uint32_t apf_acquire_time = 0;
#endif





/*
 * main.c
 *
 * init
 * no-os exec loop
 *
 */
int main(void)
{
    uint32_t wakeSource;
    uint32_t retVal;

	init();		                // Initialize pins and ports  // 425 usec  (removed 400 msec delay)

	led_power_on();             //led_green_on();     // power
	led_sampling_on();          // turn sampling/red led on momentarily for oscope trigger of init timing
	led_sampling_off();

	//printCurrentEpochTime();  //DEBUG  BUG in DEBUG
	//init_sys_data_default();     // TEMP, can't stop sampling

	//sys_data.diag = 0;   // Thomz DEBUG Menu's
	dbg_flag = sys_data.debug_flag;
	//wokebyserialFlg = 0;            // flag used during RTC deploy wakeup to handle case when woke by serial and return back to deployment 30 Dec 2021

	// Symbols for board builds are define in Project Properties (right click project, Select properties,
	// then select Build->ARM Compiler->Predefined Symbols.  Edit the BOARD_XXX and define = 1.  Only 1 board
	// should be =1, the deselected board is =0
#if BOARD_MFET == 1
    uprintf("\r\n***** ERROR: BOARD_MFET = 1 is NOT supported in this firmware version *****\r\n");
#endif
#if BOARD_MFET == 2
    dbg_printf("\r\n***** BOARD_MFET = 2 *****\r\n");
#endif
#if BOARD_MPHOX == 1
    dbg_printf("\r\n***** BOARD_MpHOx = 1 *****\r\n");
#endif
#if BOARD_NANOFET == 1
    dbg_printf("\r\n***** BOARD_NANOFET = 1 *****\r\n");
#endif
#if BOARD_MSC == 2
    dbg_printf("\r\n***** BOARD_MSC = 2  MpHOXrB + MSC2base *****\r\n");
#endif

    /*
     *
     * Re-write of the top level state machine to implement the following without as many layers
     * SLEEP/IDLE - state for a pre-deployment system
     *    - woke up by serial or reset switch
     * DEPLOY - after configuration of sampling parameters, this is the lower power state woke by
     *     A) real time clock based on sampling period
     *     B) console serial,  typically by the user to 'stop' rtc based sampling
     * COMMAND -  (polled mode) sampling period = 0
     *     A) console serial,  when sampling period = 0, it is in COMMAND, ie, polled mode and responds to 'ts' or 'sl'   STOP to get it out of DEPLOY/COMMAND mode
     * Use Cases:
     *    LABINSTRUMENT - wired, power cycled at times, typically polled. does not need to log to uSD.
     *    DATALOGGER - battery powered, self timed RTC based logging to uSD, insure the battery does not get too low to prevent uSD corruption
     *    'GLIDER" - power is being cycled.  power on, polled get_sample, save to uSD, respond to ts, sleep.  When the glider is on a down cast, power is turned off
     *    MOORED
     *    UNDERWAY SYSTEM
     *    LRAUV - streamed output
     *
     *
     */

    //state_dump();  // THOM DEBUG

    // if in wakertc mode and woke by keystroke
    //  -  X seconds to type command
    //  -  nextWakeUp is computed, goes back to sleep
    // if in wakegpio mode and woke by keystroke
    //  - X seconds to receive command or 'human serial terminal input'
    // what woke me?  WAKERTC or WAKECONSOLE (aka WakeGPIO)
    wakeSource = wake_handler();   // was sleep_handler


#define NEWMAINLOOP 1
#ifdef NEWMAINLOOP
    while(1)
    {

        //
        switch(sys_samp.state)
        {
            case DEPLOYED:      // In datalogging via RTC wakeup or console wakeup polled sampling period
                // if deployed and woke by console, user wants to run a command
                if(wakeSource == WAKE_CONSOLE)  // serial console wokeup during RTC wakeup, at issue is with Aligned to hour sampling, a sample is taken on return thru deploy(DEPLOYED)
                {
                    dbg_printf("\r\nMAIN: DEPLOYED, calling exec_command\r\n");

                    // For now, keep the old exec_command for use in deployment mode.
                    retVal = exec_command(5);        // 5 second console char timeout, return 0 to stop deployment

                    if(retVal == 0)
                    {
                        // user has stopped the deployment
                        sys_samp.state = IDLE;
                        // this returns to the while(1) with IDLE which should result in exec_main_menu
                    }
                    else
                    {
                        //wokebyserialFlg = 1;        // Wake source is console, not aborted by user so resume deployment
                        if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_DEPLOY)
                        {
                            dbg_printf("\r\nMAIN: calling deploy(DEPLOYED) after key command\r\n");
                            deploy(DEPLOYED, 1);        // wokebyserialFlg = 1, does not return (calls sleep)
                        }

                        if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_FASTSAMP)
                        {
                            databarf_loop();    //test_get_sample_loop();     // start with this for now...
                            sys_samp.state = IDLE;
                        }

                    }
                }

                if(wakeSource == WAKE_RTC)
                {
                    if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_DEPLOY)
                    {
                        // RTC wakeup
                        dbg_printf("\r\nMAIN: calling deploy(DEPLOYED)\r\n");
                        deploy(DEPLOYED, 0);               // deploy(DEPLOYED) should not return, pwrOff/sleep is called
                    }

                    if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_FASTSAMP)
                    {
                        databarf_loop();
                        sys_samp.state = IDLE;
                    }
                }

                if(wakeSource == WAKE_NOTSET)
                {
                    dbg_printf("\r\n(wakeSource == WAKE_NOTSET)\r\n");      // bug fix, code gets into weird state if firmware is downloaded while it is in deployment mode. 4 Aug 2021
                    //uprintf("\r\nmain.c main() (wakeSource == WAKE_NOTSET)\r\n");
                    sys_samp.state = IDLE;
                    storeSysDataVariables();
                }
                break;

            case IDLE:
                // wakeup path when idle, or WAKEUP_RTC is aborted by exec_command
                dbg_printf("\r\nMAIN: state = IDLE\r\n");
            default:
                if(sys_samp.state != IDLE)
                    uprintf("ERROR in sys_samp.state = %u", sys_samp.state);
                sys_samp.state = IDLE;

                //pump_sequencer_init();
                //get_key_m2m();
                //exec_main_menu();
                break;
        }

        // while(1) for handling comms
        m2m_statemachine_init();    // init serial state machine, turn off echo in ISR, print prompt
#define TIMEOUT_EXEC_MAIN   100
        exec_main_loop(TIMEOUT_EXEC_MAIN);

        if(sys_samp.state == IDLE)
        {
            sleep(IDLE, 0);
        }
        else
        {
            deploy(DEPLOYED, 1);      // 1 says woke by serial, does not take sample
        }

        //hmmm
        uprintf("\r\nlife after switch\r\n");

    }  // end of while(1)
#endif

//#define ORIGINALMAINLOOP 1
#ifdef ORIGINALMAINLOOP
    while(1)        // this while(1) handles state change in exec_main_menu
    {

        switch(sys_samp.state)
        {
            case DEPLOYED:      // RTC wakeup sampling period
                if(wakeSource == WAKE_CONSOLE)  // serial console wokeup during RTC wakeup, at issue is with Aligned to hour sampling, a sample is taken on return thru deploy(DEPLOYED)
                {
                    dbg_printf("\r\nMAIN: DEPLOYED, calling exec_command\r\n");

                    retVal = exec_command(5);        // 5 second console char timeout
                    if(retVal == 0)
                    {
                        // user has stopped the deployment
                        sys_samp.state = IDLE;
                        // this returns to the while(1) with IDLE which should result in exec_main_menu
                    }
                    else
                    {
                        wokebyserialFlg = 1;        // Wake source is console, not aborted by user so resume deployment
                        if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_DEPLOY)
                        {
                            dbg_printf("\r\nMAIN: calling deploy(DEPLOYED) after key command\r\n");
                            deploy(DEPLOYED);
                        }
                        if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_FASTSAMP)
                        {
                            databarf_loop();    //test_get_sample_loop();     // start with this for now...
                            sys_samp.state = IDLE;
                        }

                    }
                }

                if(wakeSource == WAKE_RTC)
                {
                    if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_DEPLOY)
                    {
                        // RTC wakeup
                        dbg_printf("\r\nMAIN: calling deploy(DEPLOYED)\r\n");
                        deploy(DEPLOYED);               // deploy(DEPLOYED) should not return, pwrOff/sleep is called
                    }

                    if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_FASTSAMP)
                    {
                        databarf_loop();
                        sys_samp.state = IDLE;
                    }
                }

                if(wakeSource == WAKE_NOTSET)
                {
                    dbg_printf("\r\n(wakeSource == WAKE_NOTSET)\r\n");      // bug fix, code gets into weird state if firmware is downloaded while it is in deployment mode. 4 Aug 2021
                    //uprintf("\r\nmain.c main() (wakeSource == WAKE_NOTSET)\r\n");
                    sys_samp.state = IDLE;
                    storeSysDataVariables();
                }
                break;

            case COMMAND:       // serial command sampling (period=0)
                dbg_printf("\r\nMAIN: calling deploy(COMMAND)\r\n");
                deploy(COMMAND);            // deploy(COMMAND) should not return, pwrOff/sleep is called
                break;

            case IDLE:
                dbg_printf("\r\nMAIN: state = IDLE\r\n");
            default:
                if(sys_samp.state != IDLE)
                    uprintf("ERROR in sys_samp.state = %u", sys_samp.state);
                sys_samp.state = IDLE;

                //pump_sequencer_init();
                exec_main_menu();
                break;
        }

    }  // end of while(1)
#endif



}


void print_main_menu(void)
{
    float fVbat;

    eeprom_firmware_qa_check();     // Print noisy warning about EEPROM and firmware mismatch 22 Jul 2021

#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1  || BOARD_MSC == 2
    if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] == 0)        //if(microSD_flg == 0)  6 Aug 2021
        uprintf("\r\nMicroSD card is disabled, use 1-Config -> Y-Advanced Setup -> A-App Config Fields to edit app_cfg[APPCFG_MICROSD_ENABLE]=1 to enable\r\n");    //= sys_data.app_cfg[APPCFG_MICROSD_ENABLE];

    fVbat = read_vbat();     //  print a warning if Vin is too low, especially for ymodem transfers  Bug fix 11 Mar 2022
    if( fVbat < (sys_data.low_batt_volt + 0.005) )
    {
        uprintf("\r\n\r\nVoltage input is low (%.2fv < threshold %.2fv, avoid file operations\r\n", fVbat, sys_data.low_batt_volt);
    }
#endif

    // Print out the menu:
    //uprintf("\r\n\r\n\r\nMain Menu -- %s -- Will sleep in %u seconds.   Vin = %.2f", VERSION, deployDelay, fVbat);      // VERSION is defd in system.h
    uprintf("\r\n\r\n\r\nMain Menu -- %s -- Will sleep in %u seconds", VERSION, deployDelay);      // VERSION is defd in system.h


    // better workflow mapping, addresses some of the new lab user comments
    uprintf("\r\n1 -- Configure");
    uprintf("\r\n2 -- Test");
    uprintf("\r\n3 -- Sleep");
    uprintf("\r\n4 -- Deploy");
    uprintf("\r\n5 -- Files");
    uprintf("\r\n6 -- Commandline");
    uprintf("\r\nH -- Help");
    uprintf("\r\nEnter Selection: ");

}


void eeprom_firmware_qa_check(void)
{
    //check qa version against active firmware version
    int version_match;

    //version_match = strncmp(sys_data.qa.firmware_version, VERSION, sizeof(VERSION));
    version_match = strncmp(sys_data.firmware_version, VERSION, sizeof(VERSION));

    if(version_match != 0)
    {
        uprintf("\r\n\r\n\r\n\r\n\r\n");
        uprintf("************* ERROR: EEPROM data check failed (firmware upgrade) ***********************\r\n*\r\n");
        uprintf("*  EEPROM layout must match firmware, please run EEPROM copy / edit procedure  \r\n*\r\n");
        uprintf("*  Version: %s, does not match firmware: %s\r\n*\r\n", sys_data.firmware_version, VERSION);  //         uprintf("*  QA: %s, does not match firmware: %s\r\n*\r\n", sys_data.qa.firmware_version, VERSION);
        uprintf("*  Run EEPROM data procedure:  \r\n*\r\n");
        uprintf("*     1-Config Menu -> Y -- Advanced -> E -- Edit/Copy EEPROM sys_data \r\n*\r\n");
        uprintf("*   or\r\n*\r\n");
        uprintf("*     1-Config Menu -> Y -- Advanced -> D -- assign Defaults to sys_data and sys_samp \r\n*\r\n");
        uprintf("****************************************************************************************\r\n");
    }


}



// main loop
// handle serial communications
// handle time out
void exec_main_loop(uint32_t timeout)
{

    uint32_t    startTicks;
    uint32_t    elapsedTicks;
    //uint32_t    sampleTime;
    int     retVal;
    time_t  timer;
    float   fVbat;
    char    input;

#ifdef NOCODE
    uprintf("\r\nexec_main_loop");

    if(sys_samp.state == DEPLOYED)
        uprintf("  Deployed\r\n");
    if(sys_samp.state == IDLE)
        uprintf("  Idle\r\n");
#endif


    apf_set_m2m_timeout();
    startTicks = ROM_HibernateRTCGet();
    //barfSampleTime = 5;      //5 seconds  DEBUG
    while(1)
    {
        retVal = handle_m2m_comm();
        if(retVal >= 1)
        {
            // reset timeout if serial comm
            startTicks = ROM_HibernateRTCGet();
        }
        if(retVal < 0)
        {
            // return from exec and deep sleep
            return;
        }

        elapsedTicks = ROM_HibernateRTCGet() - startTicks;
        if( (elapsedTicks > timeout))
        {
            return TIMELIMITPASSED;
        }
        //handle timeout

        elapsedTicks = ROM_HibernateRTCGet() - startTicks;
        if( (elapsedTicks > barfSampleTime) && databarFlg == 1)
        {
            startTicks = ROM_HibernateRTCGet();
            get_sample(1,1);
        }

    }

}



void exec_main_menu(void)
{
    char    input;
    int     retVal;
    time_t  timer;
    float   fVbat;

    UARTEchoSet(true);      // New 3 Jun 2024

    while(1)
    {
        print_main_menu();

        // Get user's selection, sleep after 30 sec timeout
        UARTFlushRx();

        timer = deployDelay + ROM_HibernateRTCGet(); // 30 second delay | timer = 300 + ROM_HibernateRTCGet();

        // main menu doforever loop
        while(1)
        {
            if( UARTRxBytesAvail() )
            {
                //input = get_key();      // key <CR>
                input = get_key_m2m();      // key <CR>
                //retVal = get_key_m2m();      // key <CR>
                //if(retVal >= ' ')
                  //  input = (char)retVal

                break;    // break from while(1)
            }

            if( timer <= ROM_HibernateRTCGet() )        // time has expired
            {
                // THOM 23Apr2019 - timeout on menu is resulting in a bug when SP is in sleep and a spurious character wakes it up, this results in 'deploy'
                switch(sys_samp.state)
                {
                    case DEPLOYED:      // RTC wakeup sampling period
                        // if woke up by spurious character and was deployed, go back to sleep
                        dbg_printf("\r\nEMM: DEPLOYED, calling deploy(DEPLOYED)\r\n");  // THOM DEBUG
                        deploy(DEPLOYED, 1);  // THOM new 7May2019
                        break;

                    case IDLE:
                    default:
                        dbg_printf("\r\nexec_main_menu:***** SLEEP *****\r\n");  // THOM DEBUG
                        sleep(IDLE, 0);
                        break;
                }

            }  // end of if(timeout)

        } // end of while(1) looking for key and timeout

        switch(input)
        {
            case '1':
                configuration_menu();
                break;

            case '2':
                test_menu();
                break;

            case '3':
                sleep(IDLE, 0);
                break;

            case '4':
                //deploy(START);
                deploy_start();
                break;

            //case '8':
              //  exec_command(5);
                //break;

            case '5':
#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1  || BOARD_MSC == 2
                fVbat = read_vbat();     //  print a warning if Vin is too low, especially for ymodem transfers  Bug fix 11 Mar 2022
                if( fVbat < (sys_data.low_batt_volt + 0.005) )
                {
                    uprintf("\r\n\r\nVoltage input is low (%.2fv < threshold %.2fv, avoid file operations!!!\r\n", fVbat, sys_data.low_batt_volt);
                }

                if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] == 0)   // Fix 6 Aug 2021
                {
                    uprintf("\r\nMicroSD card disabled, file commands are not available\r\n");
                    uprintf("   To enable, use 1-Config -> A-App Config to edit app_cfg[APPCFG_MICROSD_ENABLE]=1\r\n");
                }
                else
                {
                    if(microSD_flg == 1)
                    {
                        viewFiles();
                    }
                    else
                    {
                        uprintf("\r\nMicroSD card not detected, file commands are not available\r\n");
                    }
                }
#endif
#if BOARD_NANOFET > 0
                uprintf("\r\nMicroSD card is not available on NanoFET, file commands are not available\r\n");
#endif
                break;

            case 'h':
            case 'H':
                help_menu();
                break;

            case 'c':
            case 'C':
            case '6':
                //uprintf("/r/n>"); //prompt
                return;     // return from exec_main_menu
                //break;

            default:
                // if an invalid char is received, then
                break;
        }

    }

}


void new_exec_main(uint8_t input)
{
    float fVbat;

    switch(input)
    {
        case '1':
            configuration_menu();
            break;

        case '2':
            test_menu();
            break;

        case '3':
            sleep(IDLE, 0);
            break;

        case '4':
            //deploy(START);
            deploy_start();
            break;

        case '8':
            exec_command(5);
            break;

        case '5':
#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1  || BOARD_MSC == 2
            fVbat = read_vbat();     //  print a warning if Vin is too low, especially for ymodem transfers  Bug fix 11 Mar 2022
            if( fVbat < (sys_data.low_batt_volt + 0.005) )
            {
                uprintf("\r\n\r\nVoltage input is low (%.2fv < threshold %.2fv, avoid file operations!!!\r\n", fVbat, sys_data.low_batt_volt);
            }

            if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] == 0)   // Fix 6 Aug 2021
            {
                uprintf("\r\nMicroSD card disabled, file commands are not available\r\n");
                uprintf("   To enable, use 1-Config -> A-App Config to edit app_cfg[APPCFG_MICROSD_ENABLE]=1\r\n");
            }
            else
            {
                if(microSD_flg == 1)
                {
                    viewFiles();
                }
                else
                {
                    uprintf("\r\nMicroSD card not detected, file commands are not available\r\n");
                }
            }
#endif
#if BOARD_NANOFET > 0
            uprintf("\r\nMicroSD card is not available on NanoFET, file commands are not available\r\n");
#endif
            break;

        case 'h':
        case 'H':
            help_menu();
            break;

        default:
            // if an invalid char is received, then reprint
            print_main_menu();
            break;
    }
}


// Thom TODO this is used for sl command, need to fix this hack
int print_sampBuf(void)
{
    //return(uprintf("%s", sampBuf));

    int size;
    size = strlen((const char *)prnBuf);
    UARTwrite((const char *)prnBuf, size);


    return(size);
}


// get_data() is called by exec_command()
void get_data(void)
{
    int samp_byte;
    //int pumponFlg;
    char buff[81];
    FIL fileObject;
    FRESULT fresult;
    UINT br;
    bool line_end;
    //time_t current_time;        // new

    //pumponFlg = 0;

    UARTEchoSet(false);     // Turn off console echo so key hit doesn't mess up screen output

    // Open a file
    fresult = f_open(&fileObject, sys_data.fileName, FA_READ|FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        uprintf("Error.txt f_open error: %s\r\n", StringFromFresult(fresult));
    }

    // Seek to the next byte in data file since last gdata
    fresult = f_lseek(&fileObject, sys_samp.next_gdata_fptr);

    if(fresult != FR_OK)
    {
        uprintf("Error.txt f_lseek error: %s\r\n", StringFromFresult(fresult));
    }

    // Print out the records
    line_end = false;

    do
    {
        fresult = f_read(&fileObject, buff, 80, &br);
        if(fresult != FR_OK)
        {
            uprintf("Error.txt f_read error: %s\r\n", StringFromFresult(fresult));
        }

        buff[br] = '\0';

        // Wait till there is room in tx buffer, then print the buffer
        while( UARTTxBytesFree() <= 80 );
        uprintf("%s", buff);

        // Stop reading and printing if user hits key
        if(kbhit())
        {
            // Print out any remaining characters of the last sample
            do
            {
                fresult = f_read(&fileObject, buff, 80, &br);
                {
                    uprintf("Error.txt f_read error: %s\r\n", StringFromFresult(fresult));
                }

                for(samp_byte = 0; samp_byte < br; samp_byte++)
                {
                    uprintf("%c", buff[samp_byte]);

                    if( buff[samp_byte] == '\n' )   // Look for the end of the line
                    {
                        line_end = true;
                        break;
                    }
                }
            } while(br == 80 && !line_end);

            break;
        }

    } while (br == 80 && !line_end);        // Keep doing this until the end of the file

    sys_samp.next_gdata_fptr = fileObject.fptr;     // Store the current file position for next gdata command
    UARTEchoSet(true);
}


/*
 * exec_command() is the execution loop for serial command processing
 * return 0 if user 'stops' deployment
 */

uint32_t exec_command(uint32_t timeout)
{
    //time_t start_time;
    int charCount, gdata, ts, stop, quit, dump, samp_byte, gh, gm, sl, ss, pumpon, pumpoff, go, resume, exit, gc, ec;  //, to, tc;   // to, tc added for Dale Graves nsFOCE Jan 2020 Thom, nsFOCE team changed requirements so it was removed.
    int pumponFlg;
    char buff[81];
    FIL fileObject;
    FRESULT fresult;
    UINT br;
    bool line_end;
    time_t current_time;        // new

    pumponFlg = 0;

    if(sys_data.app_cfg[APPCFG_DEPLOY_UARTECHO] == 0)
    {
        UARTEchoSet(false);  // 18 Aug 2021 Disable UART echo in deployment - this is for m2m / labview to reduce the amount of echo char parsing along with 0x0d, 0x0a echo
        // echoSet = false also disables backspace mgmt in isr
    }

    while(1)    // Command loop
    {
        if(sys_data.output == VERBOSE || sys_data.test_mode) uprintf("\r\nEnter command (ts, ss, gh, gm, gc, gdata, pumpon, pumpoff, stop): ");

        //flush the receive and transmit buffers
        UARTFlushRx();

        // get command with timeout in seconds
        charCount = getUserInputTimeLimit(buff, sizeof(buff), timeout);

        // If the time limit passed...
        if(charCount == TIMELIMITPASSED)
        {
            if(sys_data.output == VERBOSE) uprintf("\r\n\r\nNo command, resuming...");
            if(pumponFlg == 1)
            {
                pwr_aux_pwr1_off();
                uprintf("AUX-PWR1 OFF (no serial activity after %u sec, sleeping)\r\n", timeout);
            }

            return(1);      // new 21 Jul 2021, this takes it back to sleep
        }
        else
        {
            // Parse command
            gdata = strncmp(buff, "gdata", 5);      // get data
            ts = strncmp(buff, "ts", 2);            // take sample
            ss = strncmp(buff, "ss", 2);            // start sample, needs to have sl within 6 sec
            gc = strncmp(buff, "gc", 2);            // get calibration coefficients
            ec = strncmp(buff, "ec", 2);            // export calibration coefficients in cal file format
            gh = strncmp(buff, "gh", 2);            // get data header
            gm = strncmp(buff, "gm", 2);            // get metadata header
            pumpon = strncmp(buff, "pumpon", 6);
            pumpoff = strncmp(buff, "pumpoff", 7);
            dump = strncmp(buff, "dump", 4);        // dump
            go = strncmp(buff, "go", 2);            // go / resume
            resume = strncmp(buff, "resume", 6);    // resume
            stop = strncmp(buff, "stop", 4);        // stop deployment (sampling)
            exit = strncmp(buff, "exit", 4);        // stop deployment (sampling)
            quit = strncmp(buff, "quit", 4);        // stop deployment (sampling)


            if( (gdata == 0) && (sys_data.app_cfg[APPCFG_MICROSD_ENABLE] == 1) )    // Display samples since last gdata command 6 Aug 2021 Fixed
            {
                get_data();
            }

            // Take a sample and continue with deployment
            else if( ts == 0 )
            {
                //get_sample(1,1);
                pump_and_sample(1,1);       // 10 Apr 2022
                return(1);

            }
            else if( ss == 0 )
            {
                //flush the receive and transmit buffers
                UARTFlushRx();

                //get_sample(0,1);
                pump_and_sample(0,1);       // 10 Apr 2022

                // Wait for command 6 seconds, then back to sleep (and dump the data)
                charCount = getUserInputTimeLimit(buff, sizeof(buff), 6);

                if(charCount == TIMELIMITPASSED)  // if retVal is -1, timeout occurred
                {
                    if(sys_data.output == VERBOSE) uprintf("\r\n\r\nNo command, resuming deployment...");
                    return(1);
                }
                else
                {
                    // Parse command
                    sl = strncmp(buff, "sl", 2);            // send last sample, must have sent ss first
                    if( sl == 0 )
                    {
                        print_sampBuf();
                        wait_consoleTx();

                        return(1);

                    }
                    else
                    {
                        uprintf("\r\nexpected sl, received %s\r\n", buff);
                        // no return or break, should put the code back to looking for next command
                    }
                }

            }
            // Get cal coeff and dump to terminal
            else if( gc == 0 )
            {
                get_pH_sensor_cal_coeff();
                return(1);
            }
            // export cal coeff in cal file format
            else if( ec == 0 )
            {
                export_pH_sensor_cal_coeff();
                return(1);
            }
            // Get header info and dump to terminal
            else if( gh == 0 )
            {
                get_header();
                return(1);
            }
            // Get header info and dump to terminal
            else if( gm == 0 )
            {
                // THOM TODO:  Bug, sending gm while in DEPLOYED mode stops the sampling
                current_time = ROM_HibernateRTCGet();       // THOM TODO: add var called sys_data.deployment_time 20 Apr 2021
                print_metadata_header(current_time);

                return(1);      // return 1 says sleep appropriately

            }
#if BOARD_MPHOX >= 1  || BOARD_MSC == 2
            else if( pumpon == 0 )
            {
                pumponFlg = 1;
                uprintf("AUX-PWR1 ON\r\n");
                pwr_aux_pwr1_on();
                //return(1);              // new 21 Jul 2021
            }
            else if( pumpoff == 0 )
            {
                pumponFlg = 0;
                uprintf("AUX-PWR1 OFF\r\n");
                pwr_aux_pwr1_off();
                //return(1);              // new 21 Jul 2021
            }
#endif
            // Exit deployment and return to main menu
            else if( (stop == 0) || (exit == 0) || (quit == 0))
            {

                sys_samp.state = IDLE;
                UARTEchoSet(true);
                storeSysSampFRAMVariables();  // update sys_samp.state in FRAM
                return(0);
            }
            else if( dump == 0)
            {
                state_dump();
                // with no return, more commands can be entered
                //return(1);
            }
            else if( go == 0 )
            {
                return(1);
            }
            else if( resume == 0 )
            {
                return(1);
            }
            // Invalid command
            else
            {
                if(sys_data.output == VERBOSE) uprintf("\r\nInvalid command");    // Goes back to top of command loop
                uprintf("NAK\r\n");
            }
        }
        dbg_printf("\r\nbottom of command loop\r\n");   // THOM debug
    }   // End of command loop

    return(1);
}

uint32_t compute_nextWakeUp(void)
{
    time_t current_time;

    // Calculate the next sample time
    if(sys_data.sample_aligned)     // Sample time hour aligned?
    {
        current_time = ROM_HibernateRTCGet();
        //sys_data.deploy_time = current_time;        // new 20 Apr 2021, removed 25 May 2021

        sys_samp.nextWakeUp = (current_time / 3600) * 3600;         // Find last hour

        // THOM TODO: 2 Jul ??? this code needs an error check to insure time is not whacked - have seen a hang here
        do
        {
            sys_samp.nextWakeUp += sys_data.sampling_period;
        } while(sys_samp.nextWakeUp < current_time);
    }
    else
    {
        sys_samp.nextWakeUp += sys_data.sampling_period;   // Not hour aligned
    }
    return(sys_samp.nextWakeUp);

}


void deploy_start(void)
{
    uint32_t retVal;
    time_t current_time, usersTime;

    char input, buff[100];
    int response;
    uint32_t temp;

    // THOM 17Mar2022, comment out: sys_data.test_mode = 0;         // SBE pump will run if not in test mode

    // Set custom start time, test mode, removed before 2018...

    // Start immediately
    current_time = ROM_HibernateRTCGet();

    sys_samp.nextWakeUp = current_time;
    sys_data.deploy_time = current_time + 1;        //added +1 to prevent time rollover  25 May 2021



#ifdef ALIGN_FIRSTSAMPLE   // Per Yui, this is OK to have the first sample 'not aligned', it's useful to see the first sample right away especially if on a 30 min schedule
    if(sys_data.sample_aligned)     // Sample time hour aligned?
    {
        current_time = ROM_HibernateRTCGet();
        //sys_data.deploy_time = current_time;        // new 20 Apr 2021, removed 25 May 2021

        sys_samp.nextWakeUp = (current_time / 3600) * 3600;         // Find last hour
        //sys_samp.nextWakeUp = ((current_time+1) / 3600) * 3600;    // new 26 May 2021


        // THOM TODO: 2 Jul ??? this code needs an error check to insure time is not whacked - have seen a hang here
        do
        {
            sys_samp.nextWakeUp += sys_data.sampling_period;
        } while(sys_samp.nextWakeUp < current_time);
    }
#endif

    //THOM 17Mar2022 commentout: sys_samp.pump_seq_state = 1;   // initialize pump sequence variable  THOM TODO - is this the right init?
    pump_and_sample_start();

    if(sys_data.pump1_ontime > 0)       // THOM todo, this should be pump_seq_type != 0
    {
        uprintf("\r\nSBE pump will run and must be immersed at start time!\r\n");

        uprintf("\r\n Delayed start feature is disabled... Next pHFET-SOS release should have it, feedback on delay parm entry is requested. \r\n");

        sys_data.test_mode = 0;
        response = yesOrNoMenuChoice("\r\n\r\n    Pump Test Mode ([Y] allows testing without activating pump)? (Y/N) [N]? ", NO);
        if(response == YES)
        {
            uprintf("\r\nPump test mode active\r\n");
            sys_data.test_mode = 1;
        }

        // get response
        //response = yesOrNoMenuChoice("\r\n\r\n    Do you wish to enter a pump delayed start? (Y/N) [N]? ", NO);
        sys_data.start_pump_time = 0;       // new 10 Apr 2022, appears to be non-initialized, this is the means for a delay before pump starts
        sys_samp.start_pump_cntdown = 0;    // new 10 Apr 2022, try to fix broken statemachine
        response = yesOrNoMenuChoice("\r\n\r\n    Enter pump delayed start? (Y/N) [N]? ", NO);
        if(response == YES)
        {
            // enter as a delay in min
            response = yesOrNoMenuChoice("\r\n\r\n    Pump start delay entry in Minutes (Y) or DateTime (N)? (Y/N) [Y]? ", YES);
            if(response == YES)
            {
                sys_data.start_pump_time = 0;     // in seconds
                temp = 0;
                //uprintf("\r\nEnter delay [%u] min: ", sys_data.start_pump_time/60);     // sys_samp.start_pump_cntdown
                uprintf("\r\nEnter delay [%u] min: ", temp);
                if(getUserInput(buff, 5))
                {
                    if(sscanf(buff, "%u", &temp) == 1)
                    {
                        // THOM TODO: check if valid start time (> 0, not too far in the future)

                        sys_data.start_pump_time = ROM_HibernateRTCGet() + temp*60;
                        sys_samp.start_pump_cntdown = temp*60;        // cntdown is in seconds
                        uprintf("\r\nPump start delay is %d minutes, pump starts at ", temp);
                        print_time_t_Time(sys_data.start_pump_time);
                    }
                }
            }
            else
            {
                // response == NO, enter datetime
                // get user delay until datetime
                usersTime = getDateAndTime();

                if(usersTime)
                {
                    uprintf("\r\nPump will start at: ");
                    print_time_t_Time(usersTime);           // print timedate
                    if(sys_data.GMT) uprintf(" GMT");
                    else uprintf(" Local");


                    sys_data.start_pump_time = usersTime;       // seconds since 1980
                    sys_samp.start_pump_cntdown = usersTime - ROM_HibernateRTCGet();    // in seconds
                    uprintf("\r\nPump delay %d minutes\r\n", sys_samp.start_pump_cntdown/60);


                    //uprintf("\r\n\r\nNote: Daylight Savings not observed. Use GMT when this will be an issue.\r\n");
                }

            }

        } // if response==YES, enter a delayed pump start
        else
        {
            // No entry of delayed start
            sys_data.start_pump_time = 0;
        }

    }

    //
    enter_deployment_note(30);

    // Begin deployment...
    sys_samp.current_sample = 0;    // Reset sample number
    sys_samp.state = DEPLOYED;          // assume RTC periodic wakeup sample period, unless period=0

    if(sys_data.sampling_period == 0)
    {
        uprintf("\r\n\r\nDeployed in polled mode, space to wake, then ts ...\r\n\r\n");        // THOM, polled mode  7 May 2018  (COMMAND)
        //sys_samp.state = COMMAND;
        sys_samp.state = IDLE;
    }

    if( print_write_metadata_header(current_time, 1, 1))  // const time_t currentTime, uint32_t printFlg, uint32_t writeFlg
    {
        // Error writing file (returns 1)
        sys_samp.state = IDLE;      // New 12 Apr 2021
        storeSysSampFRAMVariables();  // do this in sleep...
        uprintf("\r\n\r\nFile system error, check SD Card, Aborting deployment, sleeping in IDLE state\r\n");
        wait_consoleTx();
        sleep(IDLE, 0);
        return;     // File access error, abort deployment
    }

    //uprintf("\nHit a key, then type 'stop' to end deployment\n");

    uprintf("\r\n\r\nStarting deployment...       Hit a key, then type 'stop' to end deployment.");

    if(sys_data.pump1_ontime > 0)
        uprintf("  Ctrl-X <Enter> to terminate pump cycle");

    uprintf("\r\n");   // THOM New 07 Jun 2018 - added crlf

    //BUG 28 Dec 2021 - reported by Yui, data header is not written (due to missing f_close in print_write_data_header)
    print_write_data_header(1,1);   // print and write data header

    wait_consoleTx();

    storeSysSampFRAMVariables();

    if(sys_samp.state == DEPLOYED)
    {
        sleep(DEPLOYED, sys_samp.nextWakeUp);   // Sleep with RTC wakeup, nextWakeUp is 'now' in sec (arg ignored, calc'd in sleep THOM TODO, fix this
    }
    else
    {
        sleep(IDLE, 0);         // Just IDLE and DEPLOYED (WAKEGPIO and WAKERTC)
    }

}

/**************************************************************************************
 * deploy()
 * Accepts a deployment state argument that determines whether to start the deployment,
 * take a sample, or enter the command mode.
 ***************************************************************************************/
void deploy(deploy_state state, unsigned int wokebyserialFlg)
{

    uint32_t retVal;
	time_t current_time, usersTime;

    char input, buff[100];
    int response;
    uint32_t temp;

	switch(state)
	{
        case DEPLOYED:      // DEPLOY_AFTER_RTC_WAKEUP
            // RTC wakeup, take sample, compute next wakeup
            // ALIGN  Might have been woken by rs232, then timeout and drop thru here.   Causes sampling.  adding a global flag
            // if data is aligned, need to compute if this is time to sample or return to sleep
            // Note: if the wakeup happens with in the serial timeout (in exec_command(5)), then a sample is skipped unless getUserInputTimeLimit() in user_io.c checks for the proper sample time and does the sample there (big HACK)
            // Fix 30 Dec 2021
            if(wokebyserialFlg == 0)
            {
                // Take sample
                pump_and_sample(1,1);           // New v4, delay before sampling, pump sequencer, get_sample called from pump_and_sample();, board specifics handled in pump_and_sample
            }

            compute_nextWakeUp();       // result in sys_samp.nextWakeUp
            sleep(DEPLOYED, sys_samp.nextWakeUp);   // Sleep until woken by RTC or keystroke
            break;

      //  case  DEPLOY_AFTER_CONSOLE_WAKEUP:
      //      compute_nextWakeUp();       // result in sys_samp.nextWakeUp
      //      sleep(DEPLOYED, sys_samp.nextWakeUp);   // Sleep until woken by RTC or keystroke
      //      break;


        case IDLE:      // should NOT ever need to handle IDLE being passed to DEPLOY 12 Apr 2021   Case not handled in old code
        default:
            uprintf("\r\n\r\nDeploy() Should NOT be IDLE, check arg error\r\n\r\n");
            wait_consoleTx();
            sleep(IDLE, 0);
            break;

	}

}



void state_dump(void)
{
    time_t current_time;

    dbg_flag = 1;

    dbg_printf("\r\n\r\n");

    if(sys_samp.state == DEPLOYED)
    {
        dbg_printf("DEPLOYED\r\n");
    }
#ifdef NOCODE
    if( sys_samp.state == COMMAND)
    {
        dbg_printf("COMMAND\r\n");
    }
#endif
    if( sys_samp.state == IDLE)
    {
        dbg_printf("IDLE\r\n");
    }

    current_time = ROM_HibernateRTCGet();

    dbg_printf("now %lu, next wake %lu, sampling period %lu\r\n", (unsigned int)current_time, sys_samp.nextWakeUp, sys_data.sampling_period);


}

// called from tm4c123gh6pXX_startup_ccs.c
void faultISR_handler(void)
{
    uprintf("\n\npHFET-SOS Crash!!!, entering FaultISR infinte loop.  Hit Reset and report problem\n\n");
    wait_consoleTx();
    delay_msec(1000);

    while(1)
    {

    }
}

