/*
 * 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
 */
// 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 int microSD_flg;

extern unsigned char prnBuf[];

struct systemData sys_data;


struct systemSamplingData sys_samp;



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

unsigned int deployDelay = 30;      // 30 sec timeout

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

/*
 * main.c
 */

// TODO:
//  - 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

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();

	//while(1)
	//uprintf("\r\n***** WTF *****\r\n");

	//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

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

    // Databarf feature 6 Oct 2022
    // Config databarf, this causes swicheroni = 2
    // Deploy

    if (sys_data.app_cfg[APPCFG_EXEC_SWITCHERONI] == EXEC_EDDY_COVARIANCE)
    {
#if BOARD_NANOFET == 1
        uprintf("\n***** ERROR: BOARD_NANOFET = 1 is NOT supported as front-end pH sensor *****\n");
#endif
#if BOARD_MFET >= 1
        //phf_init(); // no longer compatible with optode; expects link to phb on optode port
        // will overwrite and go back to optode compatibility whenever main-mode is changed back to default, it goes back to sleep, init() is called, and main() bypasses this section
        //phf_sortInstructions(); // this has the infinite loop, which can be exited with 3X ctrl-x
#endif
    }

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

    }

}

#define MENU    1

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
    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


#if MENU == 0
    uprintf("\r\n1 -- Configure");
    uprintf("\r\n2 -- Deploy");
    uprintf("\r\n3 -- Test");
    uprintf("\r\n4 -- Sleep");
    uprintf("\r\n5 -- Files");
    uprintf("\r\nH -- Help");
#endif

#if MENU == 1
    // 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\nH -- Help");
#endif
#ifdef PROPOSED_USER_FLOW   Rejected due to legacy
    else
    {
        // Experimental - better workflow mapping
        uprintf("\r\n1 -- Configure");
        uprintf("\r\n2 -- Test");
        uprintf("\r\n3 -- Sleep");
        uprintf("\r\n4 -- Deploy");
        uprintf("\r\n5 -- Files");
        uprintf("\r\nH -- Help");
    }
#endif
    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");
    }


}

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


    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();

        while(1)
        {
            if( UARTRxBytesAvail() )
            {
                input = get_key();      // key <CR>
                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("\r\nEMM: DEPLOYED, calling deploy(DEPLOYED)\r\n");  // THOM DEBUG
                        deploy(DEPLOYED);  // THOM new 7May2019
                        break;

                    case COMMAND:       // serial command sampling (period=0)
                        dbg_printf("\r\nEMM: COMMAND, calling deploy(COMMAND)\r\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("\r\n***** 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':
#if MENU == 0
                deploy(START);
#endif
#if MENU == 1
                test_menu();
#endif
                break;

            case '3':
#if MENU == 0
                test_menu();
#endif
#if MENU == 1
                sleep(IDLE, 0);
#endif
                break;

            case '4':
#if MENU == 0
                sleep(IDLE, 0);
#endif
#if MENU == 1
                deploy(START);
#endif

                break;

            case '5':
#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1
                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;
#ifdef NOCODE
                // DEBUG
            case 'e':
            case 'E':
                //uprintf("\r\n\r\n");
                export_pH_sensor_cal_coeff();
                break;
                // DEBUG
            case 'g':
            case 'G':
                get_pH_sensor_cal_coeff();
                break;
                // DEBUG
            case 'i':
            case 'I':
                import_pH_sensor_cal_coeff();
                break;
#endif
            default:
                // if an invalid char is received, then
                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);
}





/*
 * 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
    }

    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();

        // 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("\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
            {
                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);                              // Turn console echo back on
            }

            // 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 for 6 seconds
                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
            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);

}

/**************************************************************************************
 * 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, usersTime;

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



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

            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("\r\n\r\nFile system error, check SD Card, 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 == 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)
            {


                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

#ifdef OLDCODE
#if BOARD_MPHOX >= 1
                pump_and_sample();
#endif

#if BOARD_MFET >= 2 || BOARD_NANOFET >= 1
                get_sample(1,1);
#endif
#endif
            }

            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");
    }
    if( sys_samp.state == COMMAND)
    {
        dbg_printf("COMMAND\r\n");
    }
    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);


}


