/*
 * pHFET-SOS:  MBARI software for MFET, MpHOx and NanoFET boards, derived from Scripps Institute SeapHOx software.
 *
 * 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
 */
// 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


#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 "aux1.h"
#include "aux2.h"
#include "board_util.h"
#include "sample.h"
#include "uartstdio.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 deploy(deploy_state state);
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 unsigned char prnBuf[];

struct systemData sys_data;
struct systemDataVolatile sys_data_vol;
struct systemDataVolatile sys_data_chg;         // used to write to hibernate module battery backed register file

struct systemSamplingData sys_samp;

unsigned char keytape_buf[132];         // 128 bytes with 4 byte pad
struct keytapeVars  keytape;

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

unsigned int deployDelay = 30;      // 30 sec timeout

unsigned int wokebyserialFlg;

/*
 * main.c
 */

int main(void)
{

    uint32_t wakeSource;
    uint32_t retVal;

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

	led_green_on();
	led_red_on();

	//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("\n***** ERROR: BOARD_MFET = 1 is NOT supported in this firmware version *****\n");
#endif
#if BOARD_MFET == 2
    dbg_printf("\n***** BOARD_MFET = 2 *****\n");
#endif
#if BOARD_MPHOX == 1
    dbg_printf("\n***** BOARD_MpHOx = 1 *****\n");
#endif
#if BOARD_NANOFET == 1
    dbg_printf("\n***** BOARD_NANOFET = 1 *****\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 deployed mode and woke by keystroke
    //   5 seconds to type command
    //   nextWakeUp is computed, goes back to deploy
    // what woke me?
    wakeSource = sleep_handler();   // new 25 May 2021, fixes the keystroke bug

    // THOM DEBUG
    //sys_samp.state = DEPLOYED;
    //wakeSource = WAKE_CONSOLE;

    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("\nMAIN: DEPLOYED, calling exec_command\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;
                        // ALIGN
                        // THOM ALIGN Fix sample problem when RS232 wakeup happens and then timeout
                        //compute_nextWakeUp(); // 29 Dec 2021  DID NOT FIX THE PROBLEM
                        dbg_printf("\nMAIN: calling deploy(DEPLOYED) after key command\n");
                        deploy(DEPLOYED);

                    }
                }
                if(wakeSource == WAKE_RTC)
                {
                    // RTC wakeup
                    dbg_printf("\nMAIN: calling deploy(DEPLOYED)\n");
                    deploy(DEPLOYED);               // deploy(DEPLOYED) should not return, pwrOff/sleep is called
                }
                if(wakeSource == WAKE_NOTSET)
                {
                    dbg_printf("\n(wakeSource == WAKE_NOTSET)\n");      // bug fix, code gets into weird state if firmware is downloaded while it is in deployment mode. 4 Aug 2021
                    //uprintf("\nmain.c main() (wakeSource == WAKE_NOTSET)\n");
                    sys_samp.state = IDLE;
                    storeSysDataVariables();
                }
                break;

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

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

                exec_main_menu();
                break;
        }

    }

}

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

    if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] == 0)        //if(microSD_flg == 0)  6 Aug 2021
        uprintf("\nMicroSD card is disabled, use 1-Config -> E-EEPROM to edit app_cfg[APPCFG_MICROSD_ENABLE]=1 to enable\n");    //= sys_data.app_cfg[APPCFG_MICROSD_ENABLE];

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

    uprintf("\n1 -- Configure");
    uprintf("\n2 -- Deploy");
    uprintf("\n3 -- Test");
    uprintf("\n4 -- Sleep");
    uprintf("\n5 -- Files");
    uprintf("\nH -- Help");

#ifdef SUGGESTEDFLOWCODE
    else
    {
        // Experimental - better workflow mapping
        uprintf("\n1 -- Configure");
        uprintf("\n2 -- Test");
        uprintf("\n3 -- Sleep");
        uprintf("\n4 -- Deploy");
        uprintf("\n5 -- Files");
        uprintf("\nH -- Help");
    }
#endif
    uprintf("\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));
    if(version_match != 0)
    {
        uprintf("\n\n\n\n\n");
        uprintf("************* ERROR: EEPROM data check failed (firmware upgrade) **************\n*\n");
        uprintf("*  EEPROM layout must match firmware, please run EEPROM copy / edit procedure  \n*\n");
        uprintf("*  QA: %s, does not match firmware: %s\n*\n", sys_data.qa.firmware_version, VERSION);
        uprintf("*  Run EEPROM data procedure:  1-Config Menu -> E -- Edit/Copy EEPROM sys_data \n*\n*\n");
        uprintf("*******************************************************************************\n");
    }


}

void exec_main_menu(void)
{
    char input;
    time_t timer;


    while(1)
    {
        print_main_menu();

        // Get user's selection with a 30 sec timeout - enter deployment mode --1/24/2017 -TW
        UARTFlushRx();

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

        while(1)
        {
            if( UARTRxBytesAvail() )
            {
                input = get_key();
                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'
                 //deploy(START); //enter deployment mode | if( timer <= ROM_HibernateRTCGet() ) sleep(IDLE, 0);
                switch(sys_samp.state)
                {
                    case DEPLOYED:      // RTC wakeup sampling period
                        // if woke up by spurious character and was deployed, got back to sleep
                        dbg_printf("\nEMM: DEPLOYED, calling deploy(DEPLOYED)\n");  // THOM DEBUG
                        deploy(DEPLOYED);  // THOM new 7May2019
                        break;

                    case COMMAND:       // serial command sampling (period=0)
                        dbg_printf("\nEMM: COMMAND, calling deploy(COMMAND)\n");  // THOM DEBUG
                        deploy(COMMAND);  // THOM new 12Apr2021
                        break;

                    case START:         // This state should never happen (I hope). 12Apr2021
                        deploy(START);
                        break;

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

            }  // end of if(timeout)

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

        //if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 0)
        //{

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

                case '2':
                    deploy(START);
                    break;

                case '3':
                    test_menu();
                    break;

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

                case '5':
                    viewFiles();
                    break;

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

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

        //}
    }

}




void help_menu(void)
{

    char input;
    time_t timer;

    uprintf("\n\nVersion -- %s, %s \n ", VERSION, NONVOLATILEMEM);      // VERSION is defd in system.h
#if BOARD_MFET >= 1
    //uprintf("\nMFET Power Consumption per Sample with min data config = ~0.2 Joules per sample:  12v, 1.2sec, ~14.5mA => 12 * 14.5 * (1.2 / 3600) * 3.6 = ~0.2 Joules\n\n");
#endif
#if BOARD_MPHOX == 1
    //uprintf("\nMPHOX Power Consumption per Sample with min data config = ~0.2 Joules per sample:  12v, 1.8sec, ~14.5mA => 12 * 14.5 * (1.8 / 3600) * 3.6 = ~0.3 Joules\n\n");
#endif
#if BOARD_NANOFET >= 1
    //uprintf("\nNANOFET Power Consumption per Sample = TBD\n\n");
#endif

    // Test menu loop
    while(1)
    {
        uprintf("\n\n\nHelp Menu\n");
        uprintf("C -- Configuration help\n");
        uprintf("T -- Test help\n");
        uprintf("D -- Deployment help\n");
        uprintf("P -- pH and DuraFET Voltage Wisdom\n");
        uprintf("S -- Sampling info\n");
        if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1) uprintf("\n");
        if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1) uprintf("M/m -MicroSD Logging Enable / disable\n");
        if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1) uprintf("Q/q -Advanced menus Enable / disable\n");
        //uprintf("Q/q -Advanced menus Enable / disable\n");
        if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1) uprintf("Z/z -dbg_printf statements Enable / disable\n");
        uprintf("\n");
        uprintf("9 -- Exit to Main Menu\n");
        uprintf("\nEnter Selection [9]: ");


        UARTFlushRx();
        timer = 30000 + ROM_HibernateRTCGet();

        while(1)
        {
            if( UARTRxBytesAvail() )
            {
                input = get_key();
                break;
            }

            if( timer <= ROM_HibernateRTCGet() ) sleep(IDLE, 0);
        }

        switch(input)
        {
        case 'b':
            uprintf("\n\n\n");
            uprintf("      \\|/(_)_(_)\\|/\n");
            uprintf("       @~ (o.o) ~@\n");
            uprintf("      /___( * )___\\   NA NA\n");
            uprintf("         / `U' \\      NANA\n");
            uprintf("        (   .   )      NA !!!\n");
            uprintf("         `>---<'    You Cant Get Me...\n");
            uprintf("         _\\   /_     Bug Hunt!!!\n");
            uprintf("\n\n\n");
            break;

        case 'c':
        case 'C':
            uprintf("\n\n--------- Configuration --------------\n");
            uprintf("Configuration checklist: (use Main Menu 1--Configure)\n");
            uprintf("1 -- Set Clock\n");
            uprintf("2 -- Change File Name\n");
            uprintf("3 -- Set Deployment Parameters\n");
            uprintf("        - Sample aligned to hour? (Y/N) [Y]?\n");
            uprintf("        - Enter sample period [20] sec:   Polled Mode: configure with a period of 0 seconds\n");
            //uprintf("\n       Enter pH sample average [5] samples:");
            uprintf("        - Enter pump on time [0] sec:\n");
            uprintf("        - Enter low battery_voltage (10.5 V for MpHOx with pump, 5.5 V for MFET) [5.5 V]:\n");
            uprintf("4 -- Enter pH Sensor Calibration Coefficients\n");
            uprintf("7 -- Config Data Fields\n");
            uprintf("        - Upper case letter to enable data field, Lower case letter to disable\n");
            uprintf("        - 4 -- Use Case: MpHOx Default,  MicroSD logging is enabled, Console echo is enabled\n");
            uprintf("        - 5 -- Use Case: MpHOx m2m,    MicroSD logging is disabled, Console echo is disabled\n");
            uprintf("\n\n");
            uprintf("Optional config settings\n");
            uprintf("A -- App Config Fields     For disabling MicroSD logging, or suppressing character echo for machine-to-machine\n");
            uprintf("E -- Edit/Copy EEPROM sys_data structure     Run this after firmware upgrade to insure settings are correctly copied\n");
            uprintf("\n\n");
            wait_consoleTx();
            break;

        case 'm':
            if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1)
            {
                sys_data.app_cfg[APPCFG_MICROSD_ENABLE] = 0;
                uprintf("MicroSD card DISabled, no Logging\n\n");
                storeSysDataVariables();
            }
            break;
        case 'M':
            if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1)
            {
                sys_data.app_cfg[APPCFG_MICROSD_ENABLE] = 1;
                uprintf("MicroSD card Enabled for Logging\n\n");
                storeSysDataVariables();
            }
            break;

        case 't':
        case 'T':
            uprintf("\n\n--------- Test Mode commands ---------\n");
            uprintf("Test menu: (use Main Menu 3--Test)\n");
            uprintf("\n");
            uprintf("Run QA and setup after board manufacture to initialize EEPROM and configuration defaults - CLEARS EEPROM MetaData and config, restores defaults\n");
            uprintf("\n");
            uprintf("For 24bit ADC and Analog board bringup, use 8 -- Test 24bit ADC and MUX channels\n");
            uprintf("\n");
            uprintf("Use d -- Data Header to see the data fields configured for logging as well as sampling sequence timing, Use D for continuous sampling\n");
            uprintf("\n");
            uprintf("Test communications with periperhal instruments such as Aanderaa optode, conductivity, SBE37 MicroCat CTD\n");
            //uprintf("    1 -- Battery and Board Sensors\n");
            //uprintf("    2 -- Durafet voltages\n");
            //uprintf("    3 -- Calc pH using Int Ref and Thermistor\n");
            //uprintf("    4 -- Calc pH using Ext Ref, Thermistor and Salinity\n");
            //uprintf("    5 -- Communicate with Instrument\n");
            //uprintf("    6 -- Power Output Tests for AUXx and Pump\n");
            //uprintf("    8 -- Test 24bit ADC and MUX channels\n");
            //uprintf("    D -- Data Header print and test\n");
            //uprintf("    M -- MetaData print\n");
            //uprintf("    Q -- QA and setup after board manufacture\n");
            uprintf("\n\n");
            wait_consoleTx();
            break;
        case 'd':
        case 'D':
            uprintf("\n\n------ Deployment Mode commands ------\n");
            uprintf("any character to wake, then issue command:\n");
            uprintf("stop  -- stop sampling, exit deployment mode\n");
            //uprintf("gdata -- get data since last command\n");
            uprintf("ts    -- take sample\n");
            uprintf("ss    -- start sample followed by 'sl' command to send last sample, times out after 8 sec\n");
            uprintf("gh    -- get data header\n");
            uprintf("gm    -- get metadata\n");
            uprintf("Ctrl-X, then <Enter> during sampling pump cycle to end pump cycle\n");
            uprintf("\n\n");
            wait_consoleTx();
            break;
        case 'p':
        case 'P':
            uprintf("\n\n------ pH and DuraFET Voltage Wisdom ------\n");
            uprintf("\nVtherm:        ~1.1V at 20C");
            uprintf("\nVrsi:          Internal reference based pH in seawater: ~-0.9V. In original buffer solution: ~-1.1V");
            uprintf("\nVrsi_B:        Electrical bias on ISFET voltage so that 7pH is 0V with respect to internal reference");
            uprintf("\n                  Then it changes by approximately 60 mV / pH.");
            uprintf("\n                  So nominal seawater (pH~=8), VrsiBiased ~= 60mV ± 30 mV");
            uprintf("\nVrsi std:      Standard deviation should be < 50uV, typical is ~20uV");
            uprintf("\nVrse:          External reference based pH in seawater (DeepSea Durafet only) ");
            //uprintf("\nVrse_B:        Electrical bias on ISFET voltage so that 7pH is 0V with respect to external reference");
            uprintf("\nVk:            Between -1.5 and -0.7, expected to move around");
            uprintf("\nIk:            15uV/nA + amplifier offset (~800uV).  Should be < ~20nA");
            uprintf("\nIb:            1mV/nA.   Should be < 10nA");
            uprintf("\n");
            uprintf("\n");
            wait_consoleTx();
            break;
        case 'q':
            uprintf("\nExperimental menus disabled\n");
            sys_data.app_cfg[APPCFG_MENU_LEVEL] = 0;
            sys_data.diag = 0;
            break;
        case 'Q':
            uprintf("\nExperimental menus enabled, sys_data.app_cfg[APPCFG_MENU_LEVEL] = 1\n");
            sys_data.diag = 1;
            sys_data.app_cfg[APPCFG_MENU_LEVEL] = 1;
            break;
        case 'z':
            if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1)
            {
                uprintf("\nDebug Printf statements are disabled\n");
                sys_data.debug_flag = 0;
                dbg_flag = sys_data.debug_flag;
            }
            break;
        case 'Z':
            if(sys_data.app_cfg[APPCFG_MENU_LEVEL] == 1)
            {
                uprintf("\nDebug Printf statements are enabled\n");
                sys_data.debug_flag = 1;
                dbg_flag = sys_data.debug_flag;
            }
            break;
        case 's':
        case 'S':
            uprintf("\n\n--------- ADC24 Sample Times ---------------\n");

            uprintf("User variables for trials and sps:\n");
            uprintf("VTherm=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Vtherm_trials * (float)(1/(float)sys_data.Vtherm_sps)), (unsigned int)sys_data.Vtherm_trials, (unsigned int)sys_data.Vtherm_sps);
            uprintf("  Vrsi=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Vrsi_trials * (float)(1/(float)sys_data.Vrsi_sps)), (unsigned int)sys_data.Vrsi_trials, (unsigned int)sys_data.Vrsi_sps);
            uprintf("  Vrse=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Vrse_trials * (float)(1/(float)sys_data.Vrse_sps)), (unsigned int)sys_data.Vrse_trials, (unsigned int)sys_data.Vrse_sps);
            uprintf("    Ik=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Ik_trials * (float)(1/(float)sys_data.Ik_sps)), (unsigned int)sys_data.Ik_trials, (unsigned int)sys_data.Ik_sps);

            uprintf("    Vk=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Vk_trials * (float)(1/(float)sys_data.Vk_sps)), (unsigned int)sys_data.Vk_trials, (unsigned int)sys_data.Vk_sps);
            uprintf("    Ib=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Ib_trials * (float)(1/(float)sys_data.Ib_sps)), (unsigned int)sys_data.Ib_trials, (unsigned int)sys_data.Ib_sps);
            uprintf("    Vb=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Vb_trials * (float)(1/(float)sys_data.Vb_sps)), (unsigned int)sys_data.Vb_trials, (unsigned int)sys_data.Vb_sps);
            uprintf(" Vbias=%4u ms,   trials=%2u, sps=%u\n", (uint32_t)(1000*sys_data.Vbias_trials * (float)(1/(float)sys_data.Vbias_sps)), (unsigned int)sys_data.Vbias_trials, (unsigned int)sys_data.Vbias_sps);

            uprintf("\n\n");
            break;
        default:
            return;
            break;
        }
    } // while(1)


}



// 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);
}


/*
 * 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, dump, samp_byte, gh, gm, sl, ss, pumpon, pumpoff, go, resume, exit;  //, 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
    }

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

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

        // Wait for command for timeout (10) seconds
        charCount = getUserInputTimeLimit(buff, sizeof(buff), timeout);

        // If the time limit passed...
        if(charCount == TIMELIMITPASSED)
        {
            if(sys_data.output == VERBOSE) uprintf("\n\nNo command, resuming...");
            if(pumponFlg == 1)
            {
                pwr_aux_pwr1_off();
                uprintf("AUX-PWR1 OFF (no serial activity after %u sec, sleeping)\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
            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);
            stop = strncmp(buff, "stop", 4);        // stop deployment (sampling)
            dump = strncmp(buff, "dump", 4);        // dump
            go = strncmp(buff, "go", 2);            // go / resume
            resume = strncmp(buff, "resume", 6);    // dump
            exit = strncmp(buff, "exit", 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
            {
                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\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\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\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\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);                              // Turn console echo back on
            }

            // Take a sample and continue with deployment
            else if( ts == 0 )
            {

                //get_sample(1);
                fast_sample(1,1);     // new fast sampling - replaced get_sample per Yui Jun 2019
                return(1);

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

                fast_sample(0,1);     // new fast sampling - replaced get_sample per Yui Jun 2019

                // Wait for command for 6 seconds
                charCount = getUserInputTimeLimit(buff, sizeof(buff), 6);

                if(charCount == TIMELIMITPASSED)  // if retVal is -1, timeout occurred
                {
                    if(sys_data.output == VERBOSE) uprintf("\n\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("\nexpected sl, received %s\n", buff);
                        // no return or break, should put the code back to looking for next command
                    }
                }

            }
            // 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
            else if( pumpon == 0 )
            {
                pumponFlg = 1;
                uprintf("AUX-PWR1 ON\n");
                pwr_aux_pwr1_on();
                //return(1);              // new 21 Jul 2021
            }
            else if( pumpoff == 0 )
            {
                pumponFlg = 0;
                uprintf("AUX-PWR1 OFF\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) )
            {

                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("\nInvalid command");    // Goes back to top of command loop
                uprintf("NAK\n");
            }
        }
        dbg_printf("\nbottom of command loop\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);

}

/**************************************************************************************
 * 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)
{

    uint32_t retVal;
	time_t current_time;



	switch(state)
	{
        case START:
            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

            if(sys_data.pumpon_time > 0)
            {
                uprintf("\nSBE pump will run and must be immersed at start time!");
            }

            //
            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("\n\nDeployed in polled mode, space to wake, then ts ...\n\n");        // THOM, polled mode  7 May 2018  (COMMAND)
                sys_samp.state = COMMAND;
            }

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

            uprintf("\n\nStarting deployment...\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 == COMMAND)
            {
                sleep(COMMAND, 0);   // Sleep until woken by UART (keystroke at serial terminal)   THOM New 8 May 2018
            }

            if(sys_samp.state == DEPLOYED)
            {
                sleep(DEPLOYED, sys_samp.nextWakeUp);   // Sleep with RTC wakeup, nextWakeUp is 'now' +1 sec
            }

            break;

        case COMMAND:
            retVal = exec_command(10);     // look for command characters, will sleep as needed, or return.
            if(retVal == 1)
                sleep(COMMAND, 0);
            break;

        case DEPLOYED:
            // 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)
            {
                fast_sample(1,1);
            }

            compute_nextWakeUp();
            sleep(DEPLOYED, sys_samp.nextWakeUp);   // Sleep until woken by RTC or keystroke
#ifdef WAYITUSEDTOBEALIGN
            fast_sample(1,1);
            compute_nextWakeUp();
            sleep(DEPLOYED, sys_samp.nextWakeUp);   // Sleep until woken by RTC or keystroke
#endif
            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("\n\nDeploy() Should NOT be IDLE, check arg error\n\n");
            wait_consoleTx();
            sleep(IDLE, 0);
            break;

	}

}



void state_dump(void)
{
    time_t current_time;

    dbg_flag = 1;

    dbg_printf("\n\n");

    if(sys_samp.state == DEPLOYED)
    {
        dbg_printf("DEPLOYED\n");
    }
    if( sys_samp.state == COMMAND)
    {
        dbg_printf("COMMAND\n");
    }
    if( sys_samp.state == IDLE)
    {
        dbg_printf("IDLE\n");
    }

    current_time = ROM_HibernateRTCGet();

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


}



void init_keytape(void)
{

}

uint32_t store_keytape(unsigned char key)
{

}
