/*
 * sample.c
 *
 *  Created on: Jun 8, 2021
 *      Author: Thom Maughan (tm)
 */

#include "main.h"
#include "system.h"

#include "user_io.h"
#include "ads1248_iso.h"
#include "ads1248_noniso.h"

#include "board_util.h"
#include "ctd_base.h"
#include "ctd_sbe37.h"
#include "optode.h"
#include "ocr.h"
#include "sleep.h"
#include "microsd.h"
#include "fatfs/source/ff.h"
#include "uartstdio.h"  // User local version with larger RX buffer
#include "user_io.h"
#include "pwm.h"
#include "sample.h"
#include "config.h"
#include "i2c_bme280.h"
#include "i2c_fram.h"
#include "i2c_ina260.h"

#include "apf.h"

extern int uart4_stateMachine(int ctdState);
extern int uart3_stateMachine(int uart3_state);
extern int ocr_stateMachine(int ocrState);

extern struct optodeDriver optode;
extern struct microCatDriver microCat;
extern struct ctdDriver ctd;   // struct defd in ctd_base.h
extern struct ocrDriver ocr;

extern int mmsDbgFlg;

extern unsigned char uart3_prn_buf[];
extern unsigned char uart4_prn_buf[];

extern void         adc_onchip_init(void);
extern uint32_t *   read_all_adc(void);
extern void         ADS1248_gpio3(uint32_t state);

extern uint32_t exec_command(uint32_t timeout);

extern uint32_t     adc_data[];
extern int          dbg_flag;
extern int          ads1248noniso_flg;

extern struct apfBootInitGlobals apf;      //struct in apf_util.c, defd in apf.h
extern struct apfVolatileGlobals apf_fram;
extern struct apfCfgGlobals apf_eeprom;


float fBiasPos = 0.0;       // used for read_bias_bat();
float fBiasNeg = 0.0;

uint32_t gPumpTimeout;      // used in pump_stateMachine

unsigned char prnBuf[SIZEOF_PRNBUF+4];      // size is defd in system.h
//unsigned char prnBuf[SIZEOF_PRNBUF+4+512];      // size is defd in system.h
//unsigned char prnBuf[2048];      // using prnBuf in place of dbuf in isus driver (mms_spec)

#define SIZEOF_PUMPBUF  32
unsigned char pumpBuf[SIZEOF_PUMPBUF+4];

void test_get_sample_timing(void)
{
    print_config_data_fields();
    print_data_header();     // defd in system.c
    // call get sample without timing
    get_sample(1,0);

    // call get_sample with timing
    sys_data.app_cfg[APPCFG_SAMPLING_DIAG] = 1;
    uprintf("\r\r\n ---  Sample Timing (msec) ---\r\r\n");
    get_sample(1,0);
    sys_data.app_cfg[APPCFG_SAMPLING_DIAG] = 0;
}

void databarf_loop(void)
{
    //char input;
    uint32_t retVal;

    while(1)
    {
        if( UARTRxBytesAvail() )
        {
            //input = get_key();

            retVal = exec_command(5);        // 5 second console char timeout
            if(retVal == 0)
            {
                // user has stopped the deployment
                sys_samp.state = IDLE;
                break;
            }

        }
        get_sample(1,1);
    }
}


void test_get_sample_loop(void)
{
    char input;
    int pump1_state;
    int pump2_state;

    pump1_off();
    pump2_off();
    pump1_state = 0;
    pump2_state = 0;

    print_config_data_fields();

    uprintf("\r\r\n1 to toggle pump1, 2 to toggle pump2\r\r\n");

    print_data_header();     // defd in system.c

    while(1)
    {
        if( UARTRxBytesAvail() )
        {
            //input = get_key();
            input = getChar();

            if(input == '1')
            {
                // turn on pump1 if it's off, turn off if it's on
                if(pump1_state == 0)
                {
                    pump1_on();
                    pump1_state = 1;
                    uprintf(" pump1 on \r\n");
                }
                else
                {
                    pump1_off();
                    pump1_state = 0;
                    uprintf(" pump1 off \r\n");
                }
            }


            if(input == '2')
            {
                // turn on pump2 if it's off, turn off if it's on
                if(pump2_state == 0)
                {
                    pump2_on();
                    pump2_state = 1;
                    uprintf(" pump2 on \r\n");
                }
                else
                {
                    pump2_off();
                    pump2_state = 0;
                    uprintf(" pump2 off \r\n");
                }
            }

            //if((input != 1) && (input != 2) && (input != '\n'))
            if((input > '2') || (input < '1'))
            {
                break;    // break from while(1)
            }
        }

        get_sample(1,0);
    }

    pump1_off();
    pump2_off();
}

void pump_and_sample_timing_test(void)
{
    uprintf("Feature not available in this release (test_pump_and_sample_timing)\r\n");
}



void pump_and_sample_loop_test(void)
{
    // char input;
    int response;
    int  writeFlg = 0;

    print_config_data_fields();
    print_data_header();     // defd in system.c


    if(sys_data.pump1_ontime > 0)
        uprintf("\r\r\nHit Ctrl-X to terminate pump cycle followed by any key to terminate sample loop\r\r\n");
    else
        uprintf("\r\r\nHit any key to terminate sample loop\r\r\n");

    //ask the user if they want to write to file?
	prn_nl(); prn_nl(); 
    response = yesOrNoMenuChoice("    Write to File? (Y/N) [N]? ", NO);
    if(response == YES)
    {
        uprintf("\r\nWrite to file %s, activated\r\n", sys_data.fileName);
        writeFlg = 1;
    }

    pump_and_sample_start();
    pump_and_sample(1,writeFlg);           // print to console, don't write to uSD, does nothing if pump sequencer is called

//#ifdef NOCODE
    //sys_samp.pump_seq_state = 1;   // initialize pump sequence variable
    while(1)
    {
        if( UARTRxBytesAvail() )
        {
            // input =
            get_key();
            break;    // break from while(1)
        }
        pump_and_sample(1,writeFlg);           // print to console, don't write to uSD  was   get_sample(1,0);
    }
//#endif
}

// init global vars used for pump sequencer and get_sampling
void pump_var_init(void)
{
    // initialize pumpBuf -  "P1 %02d/%02d\t" fixes garbage print of pumpBuf in Test and Config (D/d) get_sample test routines 10Apr2022
    sprintf((char *)pumpBuf, "PumpSeq=%02d\t", sys_data.pump_seq_type);
}


void pump_and_sample_start(void)
{
    sys_samp.pump_seq_state = ST_SEQ_START;
}




// pump_and_sample()   - while(1) loop for calling pump_stateMachine
//
// pass in printFlg and writeFlg to support pass thru call to get_sample(printFlg, writeFlg).  pump_and sample is also called for boards that don't have pumps (yet)
// return 0 when complete
// handle deploy to first sample delay, pump sequencing prior to sampling and later pump sequencing with sampling during pumping
// replaces most calls to get_sample in the pHFET-SOS application
//
int pump_and_sample(int printFlg, int writeFlg)
{

#if BOARD_MFET >= 2 || BOARD_NANOFET >= 1 || BOARD_MSC == 3
    get_sample(printFlg, writeFlg);         // ts command both prints and writes, ss command writes
    return(0);
#endif

#if BOARD_MPHOX >= 1  || BOARD_MSC == 2
    int pump_seq_state;

    //pump_seq_state = ST_SEQ_START;      // trying to fix broken statemachine 10 Apr 2022
//    pump_seq_state = 1;
    pump_seq_state = sys_samp.pump_seq_state;       // sys_samp.pump_seq_state requires careful initialization to handle board sleep and wake for delayed sampling THOM todo 10Apr2022


    //uprintf("pss: %d\r\n", pump_seq_state);
    while(1)
    {
        pump_seq_state = pump_stateMachine(pump_seq_state, printFlg, writeFlg);

        MAP_SysCtlDelay(250 * MILLISECOND);                 // slow the loop checking for diagnostic prints...  why??
        //uprintf("pss: %d\r\n", pump_seq_state);     // DEBUG
        if(pump_seq_state == 0)
        {
            if(sys_data.test_mode == 1) uprintf("\r\nPump is finished\r\n");
            return(0);
        }


        if(pump_seq_state == ST_SEQ_WAIT_TO_START)
        {
            uprintf("\r\nST_SEQ_WAIT_TO_START\r\n");      // DEBUG
            return(ST_SEQ_WAIT_TO_START);                   // board will sleep on return
        }
    }
#endif
}




void pump_stateMachine_init(void)
{
    sys_samp.pump_seq_state = ST_SEQ_NADA;
}





// pass in a 1 to start the state machine, call pump_stateMachine in a loop until it returns 0 or <0 (error)
// this approach enables calls to get_sample in loop.
// pump_stateMachine calls get_sample
int pump_stateMachine(int pump_seq_state, int printFlg, int writeFlg)
{
#if BOARD_MPHOX >= 1  || BOARD_MSC == 2
    uint32_t timeout;
    char timeStamp[TIMESTAMPLENGTH];
    struct tm *time_struct;
    uint32_t ticks;
    char inChar;

    // handle the case of NO PUMPing required
    // TESTED, WORKS
    if(sys_data.pump1_ontime == 0)
    {
        //uprintf(" got here 1, no pump time, so just call get_sample ");

        get_sample(printFlg, writeFlg);

        sys_samp.pump_seq_state = ST_SEQ_NADA;
        return(ST_SEQ_NADA);
    }


    // Single pump (legacy pump mode) TESTED, WORKS
    //start_pump_time  // Wakeup on sampling interval (and sample), but check the 'start deployment time' before actual using pumps when sampling 16 Feb 2022
    //sys_samp.pump_seq_state  // TODO: THIS VAR should not be stored in flash since the board does not sleep during pumping
    //---------------------------- Legacy Single Pump ---------------------------------------------------------------
    if((sys_data.pump1_ontime > 0) && (sys_data.pump_seq_type == TYPE_PUMP_SINGLE))
    {
        // Pump on time
        if(sys_data.test_mode == 1)
            uprintf("\r\nPump on (test mode)\r\n");     // Don't want the pump to run in test deployment, FUTURE, do this in pump1_on routine?
        else
            pump1_on();
        led_sampling_on();      // red_led

        timeout = Timer_1ms() + sys_data.pump1_ontime*1000;     // timeout is in msec

        do
        {
            MAP_SysCtlDelay(100 * MILLISECOND);

            if( UARTRxBytesAvail() )
            {
                //inChar = get_key();  // replace with getchar  THOM todo
                inChar = getChar();
                if( (inChar == 24) || (inChar == 0x0d) )    //Ctrl_X or KEY_ENTER
                {
                    //uprintf("Pump cycle ended early by %u sec, due to 0x%02x / %u key\r\n", (timeout - Timer_1ms())/1000, inChar, (unsigned int)inChar);
                    uprintf("Pump cycle ended early due to 0x%02x / %u key\r\n", inChar, (unsigned int)inChar);
                    break;
                }
                else
                {
                    uprintf("Pump cycle active, hit Ctrl-X then <Enter> to terminate cycle\r\n");
                }
                if( sys_data.output == VERBOSE)
                    uprintf("\r\nPumping before sample, please wait...");
            }

        //} while( read_hib_rtcc() < timeout );
        } while( Timer_1ms() < timeout );

        if(sys_data.test_mode == 1)
            uprintf("\r\nPump off (test mode)\r\n");     //
        else
            pump1_off();

        if(sys_data.test_mode == 1) uprintf("Pump off\r\n");
        //led_red_off();

        // DEBUG uprintf("\r\ncalling get_sample...\r\n");
        get_sample(printFlg, writeFlg);

        sys_samp.pump_seq_state = ST_SEQ_NADA;
        return(ST_SEQ_NADA);
    }


    //--------------------------------------------------------------------------------------------------
    // BEAMSv1 - 2 pump, alternating sampling cycles
    // The first state machine implementation will be in get_sample, later this may be moved up the call chain
    if((sys_data.pump_seq_type == TYPE_2PUMP_BEAMSV1) || (sys_data.pump_seq_type == TYPE_2PUMP_BEAMSV2))
    {
        switch(pump_seq_state)      //switch(sys_samp.pump_seq_state)
        {
        case ST_SEQ_NADA:
            return(ST_SEQ_NADA);

        case ST_SEQ_START:
            //uprintf("ST_SEQ_START\r\n");
            // initialize state vars
            sys_samp.pump1_cnt = 0;
            sys_samp.pump2_cnt = 0;


            //char timeStamp[TIMESTAMPLENGTH];

#ifdef BROKENPUMPDELAYSTART
            if(sys_data.start_pump_time == 0)           // if there is not a time set, start the pump
                sys_samp.pump_seq_state = ST_SEQ_PUMP1;
#endif

            sys_samp.pump_seq_state = ST_SEQ_PUMP1;

            //get the time from the from the RTC so we can print it (to debug delayed start)
            ticks = read_hib_rtcc();
            // DEBUG
            //time_struct = localtime((const time_t*)&ticks);
            //strftime(timeStamp, sizeof(timeStamp),"%m/%d/%y %H:%M:%S", time_struct);
            //uprintf("DBG %s  ticks: %u\r\n", timeStamp, ticks);

            if ((ticks >= sys_data.start_pump_time) && (sys_data.start_pump_time > 0))
            {
                time_struct = localtime((const time_t*)&ticks);
                strftime(timeStamp, sizeof(timeStamp),"%m/%d/%y %H:%M:%S", time_struct);
                uprintf("%s  ticks: %u\r\n", timeStamp, ticks);

                sys_samp.pump_seq_state = ST_SEQ_PUMP1;
            }

#ifdef BROKENPUMPDELAYSTART
            // DOUBLE CHECK the countdown - which to use?  time or countdown?
            if(sys_samp.start_pump_cntdown > 0)
            {
                sys_samp.pump_seq_state = ST_SEQ_WAIT_TO_START;
            }
            else
            {
                sys_samp.pump_seq_state = ST_SEQ_PUMP1;     // pump_seq_state is either ST_SEQ_PUMP1 or ST_SEQ_PUMP2
            }

#endif
           // sys_samp.pump_seq_state = ST_SEQ_PUMP1;     // pump_seq_state is either ST_SEQ_PUMP1 or ST_SEQ_PUMP2

            sys_samp.pump_seq_state = ST_SEQ_PUMP1;     // fix for broken Delayed pump start

            return(sys_samp.pump_seq_state);

        case ST_SEQ_WAIT_TO_START:          // Needs to Sleep and wake on the sample_period
            // Need BOARD_SLEEP for this to work

            if(sys_samp.start_pump_cntdown > sys_data.sampling_period)
            {
                sys_samp.start_pump_cntdown -= sys_data.sampling_period;
                uprintf("%5d\b\b\b\b\b", sys_samp.start_pump_cntdown);

                if(sys_samp.start_pump_cntdown < sys_data.sampling_period)
                {
                    ticks = read_hib_rtcc();


                    //strftime(timeStamp, sizeof(timeStamp),"%m/%d/%y %H:%M:%S", time_struct);

                    if(sys_data.test_mode == 1)
                    {
                        uprintf("\r\nPump Start: ");      // DEBUG
                        print_time_t_Time(ticks);       // print NOW
                    }

                    sys_samp.pump_seq_state = ST_SEQ_PUMP1;
                }
                else
                {
                    // go back to sleep (return to pump_and_sample and exit from there)
                    sys_samp.pump_seq_state = ST_SEQ_WAIT_TO_START;

                }
            }
            else
            {
                // count down has expired, time to move to next state
                sys_samp.pump_seq_state = ST_SEQ_PUMP1;
            }
            return(sys_samp.pump_seq_state);

        case ST_SEQ_PUMP1:
            //uprintf("ST_SEQ_PUMP1, pump on\r\n");
            // count the number of times PUMP1 is turned on
            // when time expires, turn pump off, check if the cycle count means it's time to switch to PUMP2

            if(sys_data.test_mode == 1)
                uprintf("\r\nPump1 on (test mode)\r\n");     // Don't want the pump to run in test deployment
            else
                pump1_on();
            led_sampling_on();
            gPumpTimeout = Timer_1ms() + sys_data.pump1_ontime*1000;     // timeout is in msec
            sys_samp.pump_seq_state = ST_SEQ_PUMP1;
            return(ST_SEQ_PUMP1_ONTIME);

        case ST_SEQ_PUMP1_ONTIME:

            if( UARTRxBytesAvail() )
            {
                inChar = get_key();
                if( (inChar == 24) || (inChar == 0x0d) )    //Ctrl_X (CTRL-X) or KEY_ENTER
                {
                    //uprintf("Pump cycle ended early by %u sec, due to 0x%02x / %u key\r\n", (timeout - Timer_1ms())/1000, inChar, (unsigned int)inChar);
                    uprintf("Pump1 cycle ended early due to 0x%02x / %u key\r\n", inChar, (unsigned int)inChar);
                    //break;
                    gPumpTimeout = Timer_1ms() - 2;        // force the timeout
                }
                if( sys_data.output == VERBOSE)
                    uprintf("\r\nPumping before sample, please wait...");
            }


            if(Timer_1ms() > gPumpTimeout)     // timed out?
            {
                //uprintf("ST_SEQ_PUMP1, pump off\r\n");
                //uprintf("ST_SEQ_PUMP1, %d runtime, pump off, cycle %d/%d\r\n", sys_data.pump1_ontime, sys_samp.pump1_cnt, sys_data.pump1_cycles);
                sys_samp.pump1_cnt++;
                //sprintf(pumpBuf, "P1 %d/%d\tP2 %d/%d\t", sys_samp.pump1_cnt, sys_data.pump1_cycles, sys_samp.pump2_cnt, sys_data.pump2_cycles);
                sprintf((char *)pumpBuf, "P1 %02d/%02d\t", sys_samp.pump1_cnt, sys_data.pump1_cycles);
                if(sys_data.test_mode == 1)
                    uprintf("\r\nPump off (test mode)\r\n");     //
                else
                    pump1_off();


                get_sample(printFlg, writeFlg);

                if(sys_samp.pump1_cnt >= sys_data.pump1_cycles)
                {
                    sys_samp.pump2_cnt = 0;
                    sys_samp.pump1_cnt = 0;
                    // switch to pump2
                    sys_samp.pump_seq_state = ST_SEQ_PUMP2;     // next is pump2

                }
                else
                {
                    sys_samp.pump_seq_state = ST_SEQ_PUMP1;    // stay with pump1
                }

                return(ST_SEQ_NADA);        // pumping is done
            }
            return(ST_SEQ_PUMP1_ONTIME);

        case ST_SEQ_PUMP2:
            //uprintf("ST_SEQ_PUMP2, pump2 on\r\n");
            // count the number of times PUMP1 is turned on
            // when time expires, turn pump off, check if the cycle count means it's time to switch to PUMP2

            if(sys_data.test_mode == 1)
                uprintf("\r\nPump2 on (test mode)\r\n");     // Don't want the pump to run in test deployment
            else
            {
                pump2_on();     // PORTD-3 is hanging in the breeze on MFET   MAP_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_3, 0xff);
                //pump4_on();  // solenoid as a backup for Barry Octopus Respirometer
            }
            led_sampling_on();
            gPumpTimeout = Timer_1ms() + sys_data.pump2_ontime*1000;     // timeout is in msec
            sys_samp.pump_seq_state = ST_SEQ_PUMP2;
            return(ST_SEQ_PUMP2_ONTIME);

        case ST_SEQ_PUMP2_ONTIME:
            if( UARTRxBytesAvail() )
            {
                inChar = get_key();
                if( (inChar == 24) || (inChar == 0x0d) )    //Ctrl_X (CTRL-X) or KEY_ENTER
                {
                    //uprintf("Pump cycle ended early by %u sec, due to 0x%02x / %u key\r\n", (timeout - Timer_1ms())/1000, inChar, (unsigned int)inChar);
                    uprintf("Pump2 cycle ended early due to 0x%02x / %u key\r\n", inChar, (unsigned int)inChar);
                    //break;
                    gPumpTimeout = Timer_1ms() - 2;        // force the timeout
                }
                if( sys_data.output == VERBOSE)
                    uprintf("\r\nPumping before sample, please wait...");
            }

            if(Timer_1ms() > gPumpTimeout)     // Timed out
            {
                //uprintf("ST_SEQ_PUMP2, %d runtime, pump off, cycle %d/%d\r\n", sys_data.pump2_ontime, sys_samp.pump2_cnt, sys_data.pump2_cycles);
                sys_samp.pump2_cnt++;       // increment prior to print
                //sprintf(pumpBuf, "P1 %d/%d\tP2 %d/%d\t", sys_samp.pump1_cnt, sys_data.pump1_cycles, sys_samp.pump2_cnt, sys_data.pump2_cycles);
                sprintf((char *)pumpBuf, "P2 %02d/%02d\t", sys_samp.pump2_cnt, sys_data.pump2_cycles);

                pump2_off();
                //pump4_off();  // solenoid as a backup for Barry Octopus Respirometer

                get_sample(printFlg, writeFlg);

                // switch the stored state, return 0 to exit the calling loop

                if(sys_samp.pump2_cnt >= sys_data.pump2_cycles)
                {
                    sys_samp.pump2_cnt = 0;
                    sys_samp.pump1_cnt = 0;
                    // switch to pump1
                    sys_samp.pump_seq_state = ST_SEQ_PUMP1;
                }
                else
                {
                    sys_samp.pump_seq_state = ST_SEQ_PUMP2;
                }

                return(ST_SEQ_NADA);
            }
            return(ST_SEQ_PUMP2_ONTIME);

        default:
            uprintf("ERROR: ST_SEQ hit the default switch statement");
            return(ST_SEQ_NADA);

        }
    }
// how does the pump sequencer work?
// pump_stateMachine runs before ADC24 sampling.  Pumps are off when sampling.
// pump_seq_type = 0 = pump before sample if pump1_ontime > 0 (legacy mode).
// pump_seq_type = 1 = dual alternating pumps, BEAMSv1 / Barry lab
// pump_seq_type = 2 =

//#define TYPE_PUMP_SINGLE        0
//#define TYPE_2PUMP_BEAMSV1      1
//#define TYPE_2PUMP_BEAMSV2      2


#endif
    return(ST_SEQ_NADA);
}



void get_groundfault(int printFlg, int writeFlg)
{
    float fGF, fGF_std;

    ADS1248_gpio_ADG1609_mux(0);        // MUX not needed
    MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog


    fGF = pollADS1248_iso(0, 4, 10, 1, 20);       // 200msec Chan +1 -5, trials = 10, gain = user, sps = 20 Hz
    fGF_std = sys_samp.AD24_std;

    if (printFlg)
    {
        uprintf("%s: fGF=%.5f, fGF_std=%.5f",__FUNCTION__, fGF, fGF_std);
        prn_nl();
    }
}



#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_NANOFET >= 1 || BOARD_MSC == 3
/*
 * fast sample()
 * Take measurements and write them to SD card.
 * sys_data.diag = 1 turns on timing diagnostics
 *
     *  SampNum        VbatMain        VbiasPos        VbiasNeg          Vtherm        VthrmStd         TC_Dfet            Vrsi        Vrsi_std            Vrse        Vrse_std          Vrsi_B              Vk          Vk_std              Ik       Ib                Opt_PN  Opt_SN  Moxy    O2satper        TC_opt  Dphase  Bphase  Rphase  Bamp    Bpot    Ramp    Opt_rawtemp     TempC   Cond    Salt    Unk1    Date    Time            Cond_PN Cond_SN Cond    Temp     Unk1    Unk2    Unk3
    #0000004           11.95            3.64           -3.61        0.997208        0.000003          24.714        -0.055249       0.000004        -0.799259       0.000009        0.674751        -0.500849       0.000007        18222.69 -399.597        4831    856     220.421 93.265  29.895  26.032  28.323  36.656  8.333   527.3   600.9   -128.9  5860    29      -0.011  30.508  0.016   995.506 1510.343

     ---  Sample Timing ---

    Pump         =  2008,    2008
    Pwr on, init =    24,    2032
    Bias Bat     =    70,    2102
    Ib Substrate =    62,    2164
    Ik Cntr E    =    61,    2225
    Vk Cntr E    =   260,    2485
    Vtherm       =   250,    2735
    Vrsi_B       =   509,    3244
    Vrsi         =   509,    3753
    Vrse         =   509,    4262
    Uart3 St8Ma =     2,    4264
    Condo  St8Ma =    20,    4284
    Onchip ADC   =     0,    4284
    Calc TempC   =     2,    4286
    Optode       =  2119,    6405
    CTD          =   572,    6977
    format data  =     2,    6979
    #0000005           11.95            3.64           -3.61        0.997401        0.000004          24.708        -0.055247       0.000005        -0.799254       0.000008        0.674756        -0.500856       0.000005        18219.66 -399.600        4831    856     220.417 93.276  29.903  26.029  28.320  36.655  8.335   527.2   600.5   -129.2  5860    29      -0.011  30.515  0.016   995.504 1510.358        (6979 msec)

    uSD writes   =     9,    6988
    wait TX      =    22,    7010
 *
 */
void get_sample(int printFlg, int writeFlg)
{
    char timestamp[TIMESTAMPLENGTH];

    //uint32_t tickStart, ticks, tickDelta, tickEnd, tick_onchip;
    uint32_t tickStart, ticks, tickTot, tickMinVbatStart;
    struct tm *time_struct;
    unsigned long long posixtime_ms;        // tried uint64_t

    float SBE_sal;           // MicroCAT salinity   APFTODO, wire pH calc to CTD command

    float fVrse_B;
    float fVrse_B_std;
    float fVrsi_B;
    float fVrsi_B_std;
    uint32_t indx;
    int uart3_state;
    int ctdState;


    uart3_state = 0;
    ctdState = 0;

    tickTot = 0;

    tickStart = Timer_1ms();
    tickMinVbatStart = tickStart;

    if(sys_data.test_mode == 1) { prn_nl();  uprintf("Sampling...");  }

    //led_green_on();

    wdog_pet();

#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1  || BOARD_MSC == 2
    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nPump         = %5u,   %5u", ticks, tickTot);

    tickStart = Timer_1ms();
#endif

    if(sys_data.data_cfg[CFG_TIMESTAMP] == TYPE_EPOCH_MSEC)
    {
        //1648165197 - 12365360
        // = 1,635,799,837
        // 1648165439000

        sampl.timestamp = read_hib_rtcc();
        posixtime_ms = (long long)sampl.timestamp;     // was read_hib_rtcc();   // cast to unsigned long long ?

        posixtime_ms -= TI_CCS_UNIX_EPOCH_OFFSET;

        posixtime_ms *= 1000LL;      // make it msec
        sprintf(timestamp, "%13llu", posixtime_ms);
    }
    if(sys_data.data_cfg[CFG_TIMESTAMP] == TYPE_US_DATETIME)
    {
        sampl.timestamp = read_hib_rtcc();
        time_struct = get_RTC_time();
        strftime(timestamp, sizeof(timestamp), "%m/%d/%Y %H:%M:%S", time_struct);        // change format of date/time per Yui  7 jun 2018

    }

    openADS1248_iso();              // turn on isolated power to 24bit A/D converter, init SPI3 port

#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3 // BOARD_NANOFET has adc always on...
    MAP_SysCtlDelay(100*MILLISECOND);         // new, 3 Aug 2021 - BUG fix (Crash), let power supply settle.
#endif
    wdog_pet();
    //THOM tbd, does NANOFET require ADS1248_gpio?? No with latest nanofet
    ADS1248_gpio_init();            // defd in ADS1248_iso.c  (CRASH HAPPENS HERE - first call to fast_sampling is good, next call crashes.   25 Aug 2022, Irene found a problem in gpio_init (data[0] declaration, gold star for Irene!)
    // turn on sensor and battery monitoring circuit - needs at least 150msec to settle (use iso sampling as delay
    pwr_env_mon_on();    // Turn on Load switch for ENV monitoring

#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1  || BOARD_MSC == 2
//THOM TODO  ADD INA260 for pwr_vbat_mon SOMEWHERE 2 Aug 2024

    pwr_vbat_mon_on();  //MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, 0xFF);   // Turn on VBat monitoring

    MAP_SysCtlDelay(1*MILLISECOND);         // fast sampling (was 800msec)

    adc_onchip_init();
    // read_adc_all was moved below as there is 100msec recommended for battery voltage to settle to within 1%
#endif


    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nPwr on, init = %5u,   %5u", ticks, tickTot);

    tickStart = Timer_1ms();

    // for insuring 100msec settling reading input battery voltage.

    //Future: add a config parameter for sampling CTD before pH voltages or after.   Sampling CTD before pH voltages provides a
    // salinity reading for calculating pH based on that salinity reading.

    // ---- Start optode and ctd statemachines, these need to finish and be shutdown prior to pH voltage readings  -----------------
    if(sys_data.data_cfg[CFG_UART3_INST] > 0)
    {
        //start the optode driver by passing 1, when complete, the statemachine returns 0
        uart3_state = 1;
        uart3_state = uart3_stateMachine(uart3_state);     //pass in 1 to start the state machine, hereafter pass retVal

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nUart3 St8Ma = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }

    if(sys_data.data_cfg[CFG_UART4_INST] > INST_CTD_NOTDEFD)
    {
        //start the ctd driver by passing 1, when complete, the statemachine returns 0
        ctdState = 1;
        ctdState = uart4_stateMachine(ctdState);     //pass in 1 to start the state machine, hereafter pass retVal

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nCTD    St8Ma = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }

    // ---------- Read the isolated domain 24 bit ADC -----------------

    // Sampling ideas.   Read each channel fast to equalize the ADC front end
    // read 'diagnostic' channels first to buy time for stabilizing.   Bias bat, Ik, Ib, Vbias
    // read pH related channels  Vtherm, Vrsi, Vrse

    //---------------------------- diagnostic voltages --------------------------------------
    // NOT on nanoFET, config prevents.   New per Yui, add

    if(sys_data.data_cfg[CFG_BIAS_BAT_NEG] > 0)
    {
        fBiasNeg = 0.0;     // new 6 Sep 2023  For glider use case, NanoFET will report 0.0 for biasNeg.   MFET and nanoFET need to be the same for the glider use case
#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3  //
        ADS1248_gpio3(0);
        MAP_SysCtlDelay(MILLISECOND * 10);
        fBiasNeg = pollADS1248_iso(7, 6, sys_data.Vbias_trials, 1, sys_data.Vbias_sps);     // Mux 01, Chan +7 -6, trials = 1, gain = 1, sps = 40 Hz

        fBiasNeg = fBiasNeg*2.0;                        // global var
#endif
        sampl.fBiasNeg = fBiasNeg;      // THOM note, not sure where else fBiasNeg global is used
    }
    wdog_pet();
    if(sys_data.data_cfg[CFG_UART3_INST] > INST_OPTODE_NOTDEFD)
    {
        uart3_state = uart3_stateMachine(uart3_state);
//        if((Timer_1ms() - tickDelta) > 1)
//        {
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nUart3 St8Ma = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
//        }
    }
    if(sys_data.data_cfg[CFG_UART4_INST] > INST_CTD_NOTDEFD)
    {
        ctdState = uart4_stateMachine(ctdState);
        //if((Timer_1ms() - tickDelta) > 1)
        //{
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nCTD    St8Ma = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
        //}
    }
    wdog_pet();
    if(sys_data.data_cfg[CFG_BIAS_BAT_POS] > 0)
    {

#if BOARD_NANOFET >= 1
        ADS1248_gpio0(1);
        MAP_SysCtlDelay(MILLISECOND * 10);

        fBiasPos = 0.0;

        //ADCResult = pollADS1248_iso(6, 7, 5, 1, 20);    // throw away first reading
        fBiasPos = pollADS1248_iso(6, 7, sys_data.Vbias_trials, 1, sys_data.Vbias_sps);
        MAP_SysCtlDelay(MILLISECOND * 1);
        fBiasPos = pollADS1248_iso(6, 7, sys_data.Vbias_trials, 1, sys_data.Vbias_sps);
        //ADCResult = pollADS1248_iso(6, 7, 5, 1, 20);
        ADS1248_gpio0(0);
#endif
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        ADS1248_gpio3(1);
        MAP_SysCtlDelay(MILLISECOND * 10);
        fBiasPos = pollADS1248_iso(7, 6, sys_data.Vbias_trials, 1, sys_data.Vbias_sps);
#endif

        fBiasPos = fBiasPos*2.0;                        // fBiasPos is read by voltage divider
        sampl.fBiasPos = fBiasPos;
    }
    wdog_pet();
    if(sys_data.data_cfg[CFG_UART3_INST] > INST_OPTODE_NOTDEFD)
    {
        uart3_state = uart3_stateMachine(uart3_state);
        //if((Timer_1ms() - tickDelta) > 1)
        //{
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nUart3 St8Ma = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
        //}
    }
    if(sys_data.data_cfg[CFG_UART4_INST] > INST_CTD_NOTDEFD)
    {
        ctdState = uart4_stateMachine(ctdState);
        //if((Timer_1ms() - tickDelta) > 1)
        //{
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nCTD    St8Ma = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
        //}
    }
    wdog_pet();
    if((sys_data.data_cfg[CFG_BIAS_BAT_POS] > 0) || (sys_data.data_cfg[CFG_BIAS_BAT_NEG] > 0))
    //if((Timer_1ms() - tickDelta) > 1)
    {
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nBias Bat     = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }


    // --- Finish sampling and turn off external instruments (CTD / Optode) before reading pH voltages ------
    if(sys_data.data_cfg[CFG_UART3_INST] > INST_OPTODE_NOTDEFD)
    {

        while(1)
        {
            if(uart3_state == 0)            // makes sure uart3/optode port statemachine is finished
                break;
            uart3_state = uart3_stateMachine(uart3_state);

        }
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nUart3 Inst   = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }
    wdog_pet();
    if(sys_data.data_cfg[CFG_UART4_INST] > INST_CTD_NOTDEFD)
    {
        while(1)
        {
            if(ctdState == 0)            // makes sure ctd statemachine is finished
                break;
            ctdState = uart4_stateMachine(ctdState);
        }
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nUart4 Inst   = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }

    // OCR, CTD and Optode state machines when finished turn off the instrument power.

    //---------------------------- engineering data for isfet --------------------------------------

    // ----------- Ib Subst Cur, (Diagnostic) ------------------
    if(sys_data.data_cfg[CFG_I_SUBSTRATE] > 0)
    {
#if BOARD_NANOFET >= 1
        // is there a scaling change?  no
        sampl.fIb = pollADS1248_iso(2, 0, sys_data.Ib_trials, 1, sys_data.Ib_sps);
#endif
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        ADS1248_gpio_ADG1609_mux(2);
        MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        sampl.fIb = pollADS1248_iso(1, 6, sys_data.Ib_trials, 1, sys_data.Ib_sps);       // GLIDER 1 sample, 20 hz
#endif
        sampl.fIb *= SUBSTRATECURRENT_IB_SCALING;   // convert to nanoAmps (multiply by 1000)         // new 2 Apr 2021

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nIb Substrate = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }
    wdog_pet();
    // ------------ Ik, CntrE Cur, (GLIDER)  Counter Electrode Current -------------
    if(sys_data.data_cfg[CFG_I_COUNTER] > 0)
    {
#if BOARD_NANOFET >= 1
        sampl.fIk = pollADS1248_iso(3, 4, sys_data.Ik_trials, 1, sys_data.Ik_sps);
#endif
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        ADS1248_gpio_ADG1609_mux(1);
        MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog

        sampl.fIk = pollADS1248_iso(1, 5, sys_data.Ik_trials, 1, sys_data.Ik_sps);  // 3*25ms=75msec Chan +5 -1, trials = 1, gain = 1, sps = 20 Hz  //   GLIDER 40Hz
#endif
        // 15uV/nA for Ik - assuming Ik is counter electrode current.
        // V=IR  15uV = 1nA * R (15Kohm)
        sampl.fIk -= sys_data.offset_Ik;   // subtract opamp input offet voltage
        sampl.fIk *= COUNTERELECTRODE_IK_SCALE;

        sampl.fIk_std = sys_samp.AD24_std;        // Std dev does not apply with trials = 1
        sampl.fIk_std *= COUNTERELECTRODE_IK_SCALE;      // new 2 Apr 2021

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nIk Cntr E    = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }


    // Vk, Counter Electrode Voltage, CntrE Volt, (Diagnostic)
    if(sys_data.data_cfg[CFG_V_COUNTER_ELECT] > 0)
    {
#if BOARD_NANOFET >= 1
        sampl.fVk = pollADS1248_iso(3, 0, sys_data.Vk_trials, 1, sys_data.Vk_sps);
#endif
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        ADS1248_gpio_ADG1609_mux(1);
        MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog

        sampl.fVk = pollADS1248_iso(1, 6, sys_data.Vk_trials, 1, sys_data.Vk_sps);   // Mux 01, Chan +1 -6, trials = 5, gain = 1, sps = 20 Hz
#endif
        sampl.fVk_std = sys_samp.AD24_std;

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nVk Cntr E    = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }
    wdog_pet();

    //---------------------------- pH related voltages --------------------------------------

    //------------ Vtherm, Thermistor Voltage ----------------
    if(sys_data.data_cfg[CFG_V_THERMISTOR] > 0)
    {
#if BOARD_NANOFET >= 1
        sampl.fVtherm = pollADS1248_iso(5, 7, sys_data.Vtherm_trials, 1, sys_data.Vtherm_sps);
#endif
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        sampl.fVtherm = pollADS1248_iso(0, 6, sys_data.Vtherm_trials, 1, sys_data.Vtherm_sps);       // Chan +0 -6, trials = 5, gain = 1, sps = 20 Hz (per Yui 5May2021)
#endif
        sampl.fVtherm_std = sys_samp.AD24_std;

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nVtherm       = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }
    wdog_pet();
// THOM TODO: don't allow the config of unsupported data products

    //-------------- Vrsi_B, Int Ref Off, ------------------------
    if(sys_data.data_cfg[CFG_VRSI_BIASED] > 0)     // Not valid on nanoFET
    {
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        //dbg_printf("\r\nno intref on glider\r\n");
        ADS1248_gpio_ADG1609_mux(3);
        MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog

        fVrsi_B = pollADS1248_iso(1, 5, sys_data.Vrsi_trials, 1, sys_data.Vrsi_sps);  // 200msec Chan +1 -5, trials = 10, gain = user, sps = 20 Hz
        fVrsi_B_std = sys_samp.AD24_std;

#endif
#if BOARD_NANOFET >= 1
        // Int Ref with offset not applicable on nanoFET per Scott
        fVrsi_B = 0.0;
        fVrsi_B_std = 0.0;
#endif
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nVrsi_B       = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }

    wdog_pet();

    // ----------- Vrsi (aka Vint) --------------
    if(sys_data.data_cfg[CFG_VRSI] > 0)        // Not valid on nanoFET
    {
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        //dbg_printf("\r\nno Vrsi on glider, not valid on nanoFET\r\n");
        ADS1248_gpio_ADG1609_mux(3);
        MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        sampl.fVrsi = pollADS1248_iso(1, 6, sys_data.Vrsi_trials, 1, sys_data.Vrsi_sps);       // Chan +1 -6, trials = 10, gain = 1, sps = 20 Hz (10*50ms = 500ms)
        sampl.fVrsi_std = sys_samp.AD24_std;
#endif
#if BOARD_NANOFET >= 1
        sampl.fVrsi = 0.0;
        sampl.fVrsi_std = 0.0;
#endif

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nVrsi         = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }

    wdog_pet();

    // --------- Vrse_B, Ext Ref Offset, -------------
    if(sys_data.data_cfg[CFG_VRSE_BIASED] > 0)     // Not valid on nanoFET
    {
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3
        //dbg_printf("\r\nno extref on glider\r\n");
        ADS1248_gpio_ADG1609_mux(0);
        MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        fVrse_B = pollADS1248_iso(1, 5, sys_data.Vrse_trials, 1, sys_data.Vrse_sps);       // 200msec Chan +1 -5, trials = 10, gain = user, sps = 20 Hz
        fVrse_B_std = sys_samp.AD24_std;

#endif
#if BOARD_NANOFET >= 1
        fVrse_B = 0.0;
        fVrse_B_std = 0.0;
#endif
        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nVrse_B       = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }

    wdog_pet();
    // ----------- fVrse ----------------------
    if(sys_data.data_cfg[CFG_VRSE] > 0)
    {
        sampl.fVrse = 0.0;         // new 30 Mar 2021
    //#if BOARD_NANOFET >= 1
    //        sampl.fVrse = pollADS1248_iso(1, 0, sys_data.Vrse_trials, 1, sys_data.Vrse_sps);
    //#endif

#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2 || BOARD_MSC == 3     //6 (gnd) NOT available on nanoFET
        ADS1248_gpio_ADG1609_mux(0);
        MAP_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        sampl.fVrse = pollADS1248_iso(1, 6, sys_data.Vrse_trials, 1, sys_data.Vrse_sps);  // 10x50msec= 500msec  trials=10, gain=1, sps=20
#endif
#if BOARD_NANOFET >= 1
            // 6 (GND) is not connected on nanoFET
        //sampl.fVrse = pollADS1248_iso(1, 5, sys_data.Vrse_trials, 1, sys_data.Vrse_sps);  // 10x50msec= 500msec  trials=10, gain=1, sps=20 (BUG 14 Feb 2023)
        sampl.fVrse = pollADS1248_iso(1, 0, sys_data.Vrse_trials, 1, sys_data.Vrse_sps);  // 10x50msec= 500msec  trials=10, gain=1, sps=20

#endif

        sampl.fVrse_std = sys_samp.AD24_std;

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nVrse         = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }
    wdog_pet();

    // ------------- read internal adc (battery, etc), -----------------------
    // requires  adc_onchip_init(); is called and settled for 100msec or so prior to read_all_adc

    // fix for low reading of battery voltage when few fields are config'd 21 Jul 2021.   When no fields are config'd we have a crash
    while(1)
    {
        ticks = (Timer_1ms() - tickMinVbatStart);
        if(ticks > 150)      // at least 150msec for proper vbat reading
            break;
        MAP_SysCtlDelay(MILLISECOND * 5);
    }

    wdog_pet();
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2
    read_all_adc();
    read_all_adc();     // read a second time for better fidelity

    // battery voltage must be read for 'too low' check at bottom
    sampl.fVbat_main = (float)(adc_data[0]*100);
    sampl.fVbat_main /= 18193.77;      // 30 Sep 2019  11.75v is measured with Agilent meter as 12.069v
                               // must compute battery voltage as it is checked for 'too low' below

    // onchip temperature (formulae from TI example code) (NOT NEEDED)
    //fOnChipTemp = (147.5 - ((75.0*3.3 *(float)adc_data[7])) / 4096.0);

    // LM60 temp
    if(sys_data.data_cfg[CFG_BRD_TEMP] > 0)
    {
        sampl.fBrdTempC = ((((float)adc_data[4])*0.80566) - 424) / 6.25;   //VOut= (+6.25 mV/°C × T °C) + 424 mV  // LM60 temperature sensor - formula from datasheet / schematic (good job Scott)
    }

    // Humidity
    if(sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0)
    {
        // Water sensor - convert to percent
        uint32_t uiWater = (adc_data[3] * 100) / 2596;    // NOTE: Water voltage adc cnts is 2622 when pulled to 3.3v  (measured at 2.1092v on test point with agilent u1272a meter)
        if(uiWater > 100)
            uiWater = 100;

        float fHumidity = (float)( (adc_data[2] * 100) );  // VOUT=(VSUPPLY)(0.00636(sensor RH) + 0.1515), typical at 25C
        fHumidity /= 3284;
        sampl.uiHumidity = (adc_data[2] * 100) / 3284;
    }
    wdog_pet();
    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nOnchip ADC   = %5u,   %5u", ticks, tickTot);

#endif

#if BOARD_NANOFET >= 1
    sampl.fVbat_main = 0.0;
    if((sys_data.data_cfg[CFG_BRD_TEMP] > 0) || (sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0))
    {
        bme280_read_data();

    }

    if(sys_data.data_cfg[CFG_BRD_TEMP] > 0)
    {
        sampl.fBrdTempC = bme280_get_fTemperature();
    }

    //fPressure = bme280_get_fPressure();

    if(sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0)
    {
        sampl.uiHumidity = bme280_get_uiHumidity();
    }


    // onchip temperature (formulae from TI example code) (NOT NEEDED)
    //fOnChipTemp = (147.5 - ((75.0*3.3 *(float)adc_data[7])) / 4096.0);
    wdog_pet();
    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nbme280       = %5u,   %5u", ticks, tickTot);
#endif

#if BOARD_MSC == 3

    ina260_system_voltageCurrent( &sampl.fVbat_main, &sampl.fsysAmp );        //system voltage and current

    if((sys_data.data_cfg[CFG_BRD_TEMP] > 0) || (sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0))
    {
        if(bme280_read_data() == 1)
        {
            if(sys_data.data_cfg[CFG_BRD_TEMP] > 0)
            {
                sampl.fBrdTempC = bme280_get_fTemperature();
            }

            if(sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0)
            {
                sampl.uiHumidity = bme280_get_uiHumidity();
            }
        }
        else
        {
            if(sys_data.data_cfg[CFG_BRD_TEMP] > 0)
            {
                sampl.fBrdTempC = NAN;
            }

            if(sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0)
            {
                sampl.uiHumidity = 0;
            }
        }
    }

    // onchip temperature (formulae from TI example code) (NOT NEEDED)
    //fOnChipTemp = (147.5 - ((75.0*3.3 *(float)adc_data[7])) / 4096.0);

    wdog_pet();
    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nbm280 ina260 = %5u,   %5u", ticks, tickTot);

#endif
#ifdef MOVEDUP
    wdog_pet();
    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nOnchip ADC   = %5u,   %5u", ticks, tickTot);
#endif
    tickStart = Timer_1ms();

#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1  || BOARD_MSC == 2

    int stopFlg;
    stopFlg = 0;

// Check for low battery voltage. Exit and sleep if too low.
    if( sampl.fVbat_main < (sys_data.dura_vin_min_volt + 0.005) )
    {
        pwr_vbat_mon_on();
        MAP_SysCtlDelay(MILLISECOND * 10);
        adc_onchip_init();
        MAP_SysCtlDelay(MILLISECOND * 100);
        for(indx=0; indx<5; indx++)
        {
            if(indx < 4)
                uprintf("\r\nLow battery voltage (%.2f < %.2f) !\r\n", sampl.fVbat_main, sys_data.dura_vin_min_volt );
            else
                uprintf("\r\nLow battery voltage (%.2f < %.2f) ! Exiting deploy mode...\r\n", sampl.fVbat_main, sys_data.dura_vin_min_volt );
            wait_consoleTx();

            MAP_SysCtlDelay(MILLISECOND * 150);

            read_all_adc();
            read_all_adc();     // read a second time for better fidelity

            // battery voltage must be read for 'too low' check at bottom
            sampl.fVbat_main = (float)(adc_data[0]*100);
            sampl.fVbat_main /= 18193.77;

            // if the input voltage is above the low batt threshold, then go ahead with the sample.   If after 5 checks, then SLEEP in IDLE and stop sampling to prevent uSD card failure
            if(sampl.fVbat_main > sys_data.dura_vin_min_volt)
            {

                // any reading in 5 tries above the threshold is OK to write the sample.   Looking for 5 readings below threshold to end deployment.
                stopFlg = 0;
                break;
            }
            else
            {
                // if DIAG instrumented loop, don't halt  Fix 6 Aug 2021
                if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 0)
                {
                    stopFlg = 1;
                }
            }

            led_red_on();
            led_green_off();
            delay_msec(500);

            led_red_off();
            led_green_on();
            delay_msec(500);
        }

        if(stopFlg == 1)
        {
            error_store("Low battery! Exiting deploy mode");
            sleep(IDLE, 0);     // Sleep until woken by user, halt rtc wakeup by changing state to IDLE
        }
    } // Low voltage handler


#endif

    // poweroff monitoring circuitry
    pwr_vbat_mon_off();   // Turn off VBat monitoring
    pwr_env_mon_off();    // Turn off Load switch for ENV monitoring

    // Shutdown sensor power supply
    closeADS1248_iso();


    // ---------- Calculate the external and internal pH values -------------------------

    sampl.fTempC = 20.0;        //for the case where CFG_CALC_TEMP is not on and calc_ph is on
    if(sys_data.data_cfg[CFG_CALC_TEMP] > 0)
    {
        sampl.fTempC = DuraFET_temp(sampl.fVtherm, sys_data.TCOffset);

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nCalc TempC   = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }
    if(sys_data.data_cfg[CFG_CALC_PH_VRSE] > 0)
    {
        float bestTemp = 20.0;

        if(sys_data.data_cfg[CFG_UART4_INST] == INST_CTD_NOTDEFD)
            SBE_sal = sys_data.default_sal;       // for pH_ext calc  (init'd to 35.0)
        else
            SBE_sal = sys_samp.sensor_sal;

#if BOARD_MSC == 3
        // Todo sys_data.sensor_sal;
        if(apf.CTD_Time == 0)
        {
            SBE_sal = 35.0;
            bestTemp = 20.0;
            if(sys_data.data_cfg[CFG_BRD_TEMP] > 0)
            {
                bestTemp = sampl.fBrdTempC;
            }
        }
        else
        {
            SBE_sal = apf.CTD_Salinity;
            bestTemp = apf.CTD_Temp;
        }
#else
#warning JLD - something else here???
#endif // BOARD_MSC == 3

        sampl.pHext = calc_pHext(sampl.fVrse, bestTemp, SBE_sal);  // NOTE: this might be Vrse_B that is needed, need to audit the calc....   5 May 2021

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nCalc pH Ext  = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }
    if(sys_data.data_cfg[CFG_CALC_PH_VRSI] > 0)
    {
        // requires TC and fVrsi_B
        //sampl.pHint = calc_pHint(fVrsi_B, sampl.fTempC);   // per YUI  7 Jun 2018
        sampl.pHint = calc_pHint(sampl.fVrsi, sampl.fTempC);   // per YUI  28 Dec 2021

        ticks = (Timer_1ms() - tickStart);
        tickTot += ticks;
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\r\nCalc pH Int  = %5u,   %5u", ticks, tickTot);

        tickStart = Timer_1ms();
    }

    //#000000    12.03           -3.59        0.997431        0.674842        -0.055235       0.000005        -0.799272       0.000004        -0.500569       0.000005        18178.39        -399.644        24.707  18.18122

    indx = 0;
    // for column alignment between data and header, 8 chars per field  - note, a negative sign throws off the print format
    sampl.sampNum = sys_samp.current_sample;
    if(sys_data.data_cfg[CFG_SAMPLE_NUM] > 0)       { indx += sprintf((char *)&prnBuf[indx], "#%07u\t", sys_samp.current_sample);   } //0  // was %07d
    if(sys_data.data_cfg[CFG_TIMESTAMP] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%s\t", timestamp);                   } //1

    if(sys_data.data_cfg[CFG_VIN_BAT_VOLT] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fVbat_main);                } //2  // was %5.2f
    if(sys_data.data_cfg[CFG_BIAS_BAT_POS] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fBiasPos);                  } //5  5.2
    if(sys_data.data_cfg[CFG_BIAS_BAT_NEG] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fBiasNeg);                  } //5  5.2
    if(sys_data.data_cfg[CFG_BRD_TEMP] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fBrdTempC);                 } //3  // was %5.2f
    if(sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8u\t",   sampl.uiHumidity);                } //4  // was %3u

    if(sys_data.data_cfg[CFG_V_THERMISTOR] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVtherm);                   } //6
    if(sys_data.data_cfg[CFG_V_THERMISTOR_STD] > 0) { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVtherm_std);               } //7
    if(sys_data.data_cfg[CFG_CALC_TEMP] > 0)        { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.fTempC);                    } //20

    if(sys_data.data_cfg[CFG_VRSI] > 0)             { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrsi);                     } //12
    if(sys_data.data_cfg[CFG_VRSI_STD] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrsi_std);                 } //13
    if(sys_data.data_cfg[CFG_CALC_PH_VRSI] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.pHint);                     } //21

    if(sys_data.data_cfg[CFG_VRSE] > 0)             { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrse);                     } //14
    if(sys_data.data_cfg[CFG_VRSE_STD] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrse_std);                 } //15
    if(sys_data.data_cfg[CFG_CALC_PH_VRSE] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.pHext);                     } //22

    if(sys_data.data_cfg[CFG_VRSI_BIASED] > 0)      { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrsi_B);                   } //8
    if(sys_data.data_cfg[CFG_VRSI_BIASED_STD] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrsi_B_std);               } //9
    if(sys_data.data_cfg[CFG_VRSE_BIASED] > 0)      { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrse_B);                   } //10
    if(sys_data.data_cfg[CFG_VRSE_BIASED_STD] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrse_B_std);               } //11

    if(sys_data.data_cfg[CFG_V_COUNTER_ELECT] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVk);                       } //16
    if(sys_data.data_cfg[CFG_V_COUNTER_ELECT_STD] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVk_std);               } //17
    if(sys_data.data_cfg[CFG_I_COUNTER] > 0)        { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fIk);                       } //18
    if(sys_data.data_cfg[CFG_I_SUBSTRATE] > 0)      { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.fIb);                       } //19

    if(sys_data.data_cfg[CFG_UART3_INST] > INST_OPTODE_NOTDEFD)       { indx += sprintf((char *)&prnBuf[indx], "%s\t", uart3_prn_buf);   } //23
    if(sys_data.data_cfg[CFG_UART4_INST] > INST_CTD_NOTDEFD)          { indx += sprintf((char *)&prnBuf[indx], "%s\t", uart4_prn_buf);   } //23

    if(sys_data.pump_seq_type == 2)                 { indx += sprintf((char *)&prnBuf[indx], "%s\t", pumpBuf);                      } //24

    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) {prn_nl(); uprintf("format data  = %5u,   %5u", ticks, tickTot);  prn_nl(); prn_nl(); }

    tickStart = Timer_1ms();

     //uprintf("(Time: %u msec)", ticks );  //APFTODO, this is written into the data record... verify
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1)  indx += sprintf((char *)&prnBuf[indx], "(%u msec)\t", tickTot);

    // terminate.
    indx--;     // delete the trailing tab
    prnBuf[indx] = '\r';
    indx++;
    prnBuf[indx] = '\n';
    indx++;
    prnBuf[indx] = 0;

    //if(txFlg == 1) uprintf("%s", prnBuf);      // uprint maximum is 255 sample to console, leaving this line for other developers...
    if(printFlg == 1) UARTwrite((char *)prnBuf, strlen((char *)prnBuf));

    // NOTE: write to uSD happens in apf_take_sample_pH.  APFTODO, refactor and straigten out get_sample

    wait_consoleTx();

    ticks = (Timer_1ms() - tickStart);
    tickTot += ticks;
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) { prn_nl(); uprintf("wait TX      = %5u,   %5u", ticks, tickTot); }
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) { prn_nl(); uprintf("------------------------------");   }
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) { prn_nl(); uprintf("total (msec) =          %5u", tickTot);  prn_nl();  }

    sys_samp.current_sample++;  // Increment the sample counter, this is stored to FRAM in sleep() routine
#if BOARD_MSC == 3
    apf_fram.dura_sample_time = sampl.timestamp;
    apf.acquire_time = (float)tickTot;
    apf.acquire_time /= 1000.0;

    apf.gotSample = TRUE;
#endif // BOARD_MSC == 3

    led_sampling_off();      // red_led
    //led_green_off();


// .diag is gone

}




// used by send_last_sample to format the binary data into prnBuf
void print_sampl(void)
{
    unsigned int indx;
    struct tm *time_struct;
    char timestamp[TIMESTAMPLENGTH];
    unsigned long long posixtime_ms;

    if(sys_data.data_cfg[CFG_TIMESTAMP] == TYPE_EPOCH_MSEC)
    {

        posixtime_ms = (long long)sampl.timestamp;     // was read_hib_rtcc();   // cast to unsigned long long ?

        posixtime_ms -= TI_CCS_UNIX_EPOCH_OFFSET;

        posixtime_ms *= 1000LL;      // make it msec
        sprintf(timestamp, "%13llu", posixtime_ms);
    }
    if(sys_data.data_cfg[CFG_TIMESTAMP] == TYPE_US_DATETIME)
    {
        time_struct = localtime((const time_t*)&sampl.timestamp);
        strftime(timestamp, sizeof(timestamp), "%m/%d/%Y %H:%M:%S", time_struct);        // change format of date/time per Yui  7 jun 2018
    }


    indx = 0;
    // for column alignment between data and header, 8 chars per field  - note, a negative sign throws off the print format
    sampl.sampNum = sys_samp.current_sample;
    if(sys_data.data_cfg[CFG_SAMPLE_NUM] > 0)       { indx += sprintf((char *)&prnBuf[indx], "#%07u\t", sys_samp.current_sample);   } //0  // was %07d
    if(sys_data.data_cfg[CFG_TIMESTAMP] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%s\t", timestamp);                   } //1

    if(sys_data.data_cfg[CFG_VIN_BAT_VOLT] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fVbat_main);                } //2  // was %5.2f
    if(sys_data.data_cfg[CFG_BIAS_BAT_POS] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fBiasPos);                  } //5  5.2
    if(sys_data.data_cfg[CFG_BIAS_BAT_NEG] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fBiasNeg);                  } //5  5.2
    if(sys_data.data_cfg[CFG_BRD_TEMP] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fBrdTempC);                 } //3  // was %5.2f
    if(sys_data.data_cfg[CFG_BRD_HUMIDITY] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8u\t",   sampl.uiHumidity);                } //4  // was %3u

    if(sys_data.data_cfg[CFG_V_THERMISTOR] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVtherm);                   } //6
    if(sys_data.data_cfg[CFG_V_THERMISTOR_STD] > 0) { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVtherm_std);               } //7
    if(sys_data.data_cfg[CFG_CALC_TEMP] > 0)        { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.fTempC);                    } //20

    if(sys_data.data_cfg[CFG_VRSI] > 0)             { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrsi);                     } //12
    if(sys_data.data_cfg[CFG_VRSI_STD] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrsi_std);                 } //13
    if(sys_data.data_cfg[CFG_CALC_PH_VRSI] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.pHint);                     } //21

    if(sys_data.data_cfg[CFG_VRSE] > 0)             { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrse);                     } //14
    if(sys_data.data_cfg[CFG_VRSE_STD] > 0)         { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVrse_std);                 } //15
    if(sys_data.data_cfg[CFG_CALC_PH_VRSE] > 0)     { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.pHext);                     } //22

    //if(sys_data.data_cfg[CFG_VRSI_BIASED] > 0)      { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrsi_B);                   } //8
    //if(sys_data.data_cfg[CFG_VRSI_BIASED_STD] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrsi_B_std);               } //9
    //if(sys_data.data_cfg[CFG_VRSE_BIASED] > 0)      { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrse_B);                   } //10
    //if(sys_data.data_cfg[CFG_VRSE_BIASED_STD] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", fVrse_B_std);               } //11

    if(sys_data.data_cfg[CFG_V_COUNTER_ELECT] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVk);                       } //16
    if(sys_data.data_cfg[CFG_V_COUNTER_ELECT_STD] > 0)  { indx += sprintf((char *)&prnBuf[indx], "%8.6f\t", sampl.fVk_std);               } //17
    if(sys_data.data_cfg[CFG_I_COUNTER] > 0)        { indx += sprintf((char *)&prnBuf[indx], "%8.2f\t", sampl.fIk);                       } //18
    if(sys_data.data_cfg[CFG_I_SUBSTRATE] > 0)      { indx += sprintf((char *)&prnBuf[indx], "%8.3f\t", sampl.fIb);                       } //19
}


void send_last_sample(void)
{
    // read last sample for FRAM
    fram_last_samp_retrieve();

    print_sampl();  //format the binary data into prnBuf
    UARTwrite((char *)prnBuf, strlen((char *)prnBuf));

    //uprintf("TODO store and read sample from FRAM, check if stale\r\n");
}



void exercise_iso(void)
{
    float fVtherm; // , fVtherm_std;

    openADS1248_iso();              // turn on isolated power to 24bit A/D converter
    ADS1248_gpio_init();            // defd in ADS1248_iso.c

    // turn on sensor and battery monitoring circuit - needs at least 150msec to settle (use iso sampling as delay
    pwr_env_mon_on();    // Turn on Load switch for ENV monitoring
    pwr_vbat_mon_on();  //MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, 0xFF);   // Turn on VBat monitoring

    MAP_SysCtlDelay(1*MILLISECOND);         // fast sampling (was 800msec)

    adc_onchip_init();

    MAP_SysCtlDelay(100*MILLISECOND);

    fVtherm = pollADS1248_iso(0, 6, sys_data.Vtherm_trials, 1, sys_data.Vtherm_sps);       // Chan +0 -6, trials = 5, gain = 1, sps = 20 Hz (per Yui 5May2021)
    // fVtherm_std = sys_samp.AD24_std;

    uprintf("\r\nThermistor = %.6f\r\n", fVtherm);

    MAP_SysCtlDelay(900*MILLISECOND);


    // poweroff monitoring circuitry
    pwr_vbat_mon_off();   // Turn off VBat monitoring
    pwr_env_mon_off();    // Turn off Load switch for ENV monitoring

    // Shutdown sensor power supply
    closeADS1248_iso();

}

#endif




/*
 * batt_volt()
 * Returns main battery voltage on controller side of isolation diodes
 * RCG 4/14
 * TM 4/18  Added support for new MFET board
 *
 */
float batt_volt(void)
{
    float batt_volt = 0.0f;

#if BOARD_MFET >= 1 || BOARD_MPHOX == 1  || BOARD_MSC == 2
    batt_volt = read_env_sensors();
#endif

    return batt_volt;
}

/*
 * batt_volt_iso()
 * Returns isolated battery voltage
 * RCG 4/14
 * TM  8/2019 added support for MFET v2 (revB)
 * assumes that iso power is on
 */
float batt_volt_iso(void)
{
#if BOARD_MFET == 1
    float batt_volt = 0.0f;
    return(batt_volt);
#elif BOARD_MFET == 2  || BOARD_MPHOX == 1  || BOARD_MSC == 2
    // GPIO3 on ADS1248
    //    float is gnd
    //    0 = bias-
    //    1 = bias+
    float ADC_volts = 0.0f;
    float batt_volt = 0.0f;
    ADS1248_gpio3(1);
    ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);
    batt_volt = ADC_volts*2.0;
    return(batt_volt);
#else
    return 0.0f;
#endif
}

// NOT TESTED YET 14 Apr 2021 - issue with iso power turn on and init should be resolved
void read_bias_bat(void)
{
    float biasplus, biasneg, ADC_volts;
    //unsigned int indx;

    // GPIO3 on ADS1248
    //    float is gnd
    //    0 = bias-
    //    1 = bias+
    pwr_iso_on();

    openADS1248_iso();
    MAP_SysCtlDelay(MILLISECOND * 100);
    ADS1248_gpio_init();

    ADS1248_gpio3(0);
    MAP_SysCtlDelay(MILLISECOND * 10);
    ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);    // throw away first reading
    MAP_SysCtlDelay(MILLISECOND * 100);
    ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);
    biasneg = ADC_volts*2.0;

    ADS1248_gpio3(1);
    MAP_SysCtlDelay(MILLISECOND * 10);
    ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);    // throw away first reading
    MAP_SysCtlDelay(MILLISECOND * 100);
    ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);
    biasplus = ADC_volts*2.0;

    fBiasPos = biasplus;   // update global var
    fBiasNeg = biasneg;     // update global var

    //uprintf("Iso Bat Bias- %7.3F,  Bias+ %7.3F\r\n", biasneg, biasplus);
    //MAP_SysCtlDelay(MILLISECOND * 500);


    closeADS1248_iso();

    pwr_iso_off();
}

void print_bias_volt_iso(void)
{
    float biasplus, biasneg, ADC_volts;
    unsigned int indx;

    // GPIO3 on ADS1248
    //    float is gnd
    //    0 = bias-
    //    1 = bias+


    openADS1248_iso();
    MAP_SysCtlDelay(MILLISECOND * 100);
    ADS1248_gpio_init();

    for(indx=0; indx<5; indx++)
    {
        ADS1248_gpio3(0);
        MAP_SysCtlDelay(MILLISECOND * 10);
        ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);
        biasneg = ADC_volts*2.0;

        ADS1248_gpio3(1);
        MAP_SysCtlDelay(MILLISECOND * 10);
        ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);
        biasplus = ADC_volts*2.0;

        uprintf("Iso Bat Bias- %7.3F,  Bias+ %7.3F\r\n", biasneg, biasplus);
        MAP_SysCtlDelay(MILLISECOND * 500);
    }

    closeADS1248_iso();
}




/*
 * temp_int()
 * Returns internal temperature
 * RCG 4/14
 */
float controller_temp(void)
{
    float ADC_volts, temp;

    ADC_volts = pollADS1248_noniso(2, 7, 1); // Chan 2-7, 1 trial
    temp = (ADC_volts * 1000 - 480) / 15.6;
    return temp;
}


/*
 * depth()
 * Returns depth in meters from Honeywell MLH100PGB 100 psi pressure sensor
 * RCG 4/14
 */
float pressure(void)
{
#ifdef GETRIDOFPRESSURE_7May2019
    float ADCvolts, pressure;


    ADCvolts = pollADS1248_noniso(3, 7, 1); // Result in volts

    pressure = ((ADCvolts * 3.2) - 0.5) * (100 / 4 / 1.45); // 3.2-to-1 divider, 0.5-4.5 V span, 1.45 dBar/psi
    return pressure;    // Pressure in dBar
#endif
    return(0.0);
}

#if BOARD_MFET >= 1  || BOARD_MPHOX == 1  || BOARD_MSC == 2


float read_vbat(void)
{
    float fVbat;

#ifdef PRINT_THESE_VALS
    float fOnChipTemp, fExtTemp;

    uint32_t  uiHumidity, uiWater;
    uint32_t indx = 0;
#endif

    pwr_env_mon_on();       // Turn on Load switch for ENV monitoring
    pwr_vbat_mon_on();      //   MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, 0xFF);   // Turn on VBat monitoring

    adc_onchip_init();

    // delay to allow voltage divider circuit to charge up
    MAP_SysCtlDelay(MILLISECOND*150);    // 150msec reads 12.26, s100 works (12.31 reads 12.18), 25 does not (12.31 reads 8.99, 9.09, 9.20, 9.29)v), 50msec is weak (11.28, 11.31, 11.35, 11.37)

    read_all_adc();

    fVbat = (float)(adc_data[0]*100);
    //fVbat /= 18687.707;  // Thom 30 Sep 2019 reading 11.75 when actual is 12.069v
    fVbat /= 18193.77;

#ifdef PRINT_THESE_VALS
    // onchip temperature (formulae from TI example code)
    //TempValueC = (uint32_t)(147.5 - ((75.0*3.3 *(float)adc_data[7])) / 4096.0);
    fOnChipTemp = (147.5 - ((75.0*3.3 *(float)adc_data[7])) / 4096.0);

    // LM60 temperature sensor - formula from datasheet / schematic (good job Scott)
    // Vout = (+6.25 mV/C * T) + 424mV   =>  T = ((ADCcnt * 0.80566) - 424) / 6.25
    fExtTemp = ((((float)adc_data[4])*0.80566) - 424) / 6.25;

    // Water sensor - convert to percent
    uiWater = (adc_data[3] * 100) / 2596;    // NOTE: Water voltage adc cnts is 2622 when pulled to 3.3v  (measured at 2.1092v on test point with agilent u1272a meter)
    if(uiWater > 100)
        uiWater = 100;

    // Humidity
    uiHumidity = (adc_data[2] * 100) / 3284;

    // Pressure

    //uprintf("Bat = %6.2fV VBAT=%u VPRESS=%3u  VHUM=%3u  VWATER=%3u   VXTEMP=%3u   VBAT=%3u, VBAT=%u, VTEMP=%u  i=%u\r", fVbat, adc_data[0], adc_data[1], adc_data[2], adc_data[3], adc_data[4], adc_data[5], adc_data[6], TempValueC, indx++);
    dbg_printf("Bat = %6.2fV Pressure = %4u  Humidity = %3u  Water = %3u  Temperature = %5.1fC (%4u)   ChipTemp = %5.1fC  i=%u\r\n\r", fVbat, adc_data[1], uiHumidity, uiWater, fExtTemp, adc_data[4], fOnChipTemp, indx++);
#endif

    pwr_vbat_mon_off();   // Turn off VBat monitoring

    return(fVbat);
 }

// This function applies only to the MFET/MPHOX
float read_env_sensors(void)
{
    float fVbat, fOnChipTemp, fExtTemp;
    uint32_t  uiHumidity, uiWater;
    uint32_t indx = 0;

    pwr_env_mon_on();       // Turn on Load switch for ENV monitoring
    pwr_vbat_mon_on();      //MAP_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, 0xFF);   // Turn on VBat monitoring

    pwr_iso_on();        // Iso-power On
    dbg_printf("iso power on\r\n");

    adc_onchip_init();

    // delay to allow voltage divider circuit to charge up
    MAP_SysCtlDelay(MILLISECOND*150);    // 150msec reads 12.26, s100 works (12.31 reads 12.18), 25 does not (12.31 reads 8.99, 9.09, 9.20, 9.29)v), 50msec is weak (11.28, 11.31, 11.35, 11.37)

    read_all_adc();

    fVbat = (float)(adc_data[0]*100);
    //fVbat /= 18687.707;  // Thom 30 Sep 2019 reading 11.75 when actual is 12.069v
    fVbat /= 18193.77;

    // onchip temperature (formulae from TI example code)
    //TempValueC = (uint32_t)(147.5 - ((75.0*3.3 *(float)adc_data[7])) / 4096.0);
    fOnChipTemp = (147.5 - ((75.0*3.3 *(float)adc_data[7])) / 4096.0);

    // LM60 temperature sensor - formula from datasheet / schematic (good job Scott)
    // Vout = (+6.25 mV/C * T) + 424mV   =>  T = ((ADCcnt * 0.80566) - 424) / 6.25
    fExtTemp = ((((float)adc_data[4])*0.80566) - 424) / 6.25;

    // Water sensor - convert to percent
    uiWater = (adc_data[3] * 100) / 2596;    // NOTE: Water voltage adc cnts is 2622 when pulled to 3.3v  (measured at 2.1092v on test point with agilent u1272a meter)
    if(uiWater > 100)
        uiWater = 100;

    // Humidity
    uiHumidity = (adc_data[2] * 100) / 3284;

    // Pressure

    //uprintf("Bat = %6.2fV VBAT=%u VPRESS=%3u  VHUM=%3u  VWATER=%3u   VXTEMP=%3u   VBAT=%3u, VBAT=%u, VTEMP=%u  i=%u\r", fVbat, adc_data[0], adc_data[1], adc_data[2], adc_data[3], adc_data[4], adc_data[5], adc_data[6], TempValueC, indx++);
    dbg_printf("Bat = %6.2fV Pressure = %4u  Humidity = %3u  Water = %3u  Temperature = %5.1fC (%4u)   ChipTemp = %5.1fC  i=%u\n\r", fVbat, adc_data[1], uiHumidity, uiWater, fExtTemp, adc_data[4], fOnChipTemp, indx++);


    pwr_vbat_mon_off();   // Turn off VBat monitoring
    pwr_iso_off();        // Iso-power Off
    dbg_printf("iso power off, vbat monitoring off\r\n");

    return(fVbat);
 }
#endif





#ifdef TIMINGINFO
SampNum             MM/DD/YYYY HH:MM:SS          Vtherm        VthrmStd         TC_Dfet            Vrsi        Vrsi_std        pHintEst            Vrse        Vrse_std        pHextEst          Vrsi_B        VrsiBstd          Vrse_B VrseBstd              Vk          Vk_std              Ik              Ib        VbatMain        VbiasPos        VbiasNeg         TC_cont        Humidity

Pump         = 2002,   2002
Pwr on, init =   24,   2026
CTD Optode   =    0,   2026
Bias Bat     =   71,  2097
Ib Substrate =   61,   2158
Ik Cntr E    =   60,   2218
Vk Cntr E    =  260,   2478
Vtherm       =  249,   2727
Vrsi         =  508,   3235
Vrse         =  508,   3743
Vrse_B       =  507,   4250
Vrsi_B       =  508,   4758
Onchip ADC   =    0,   4758
Calc TempC   =    1,   4759
Calc pH Ext  =    2,   4761
Calc pH Int  =    0,   4761
Optode       =    0,   4761
CTD          =    0,   4761
format data  =    2,   4763
#0000492             05/17/2021 04:26:32        0.997514        0.000002          24.704        -0.055202       0.000009          18.182        -0.799227       0.000005           7.826        0.674909        0.000008        -0.069118        0.000006        -0.500551       0.000003        18197.15        -399.608           12.00            3.61           -3.60           18.01              59        (4763 msec)

uSD writes   =   12,   4775
wait TX      =   27,   4802



A -- Sample Num               = 1
B -- MM/DD/YYYY HH:MM:SS      = 1

C -- Vthermistor              = 1
     Vtermistor Std           = 1
D -- Temperature Calc         = 1
E -- Vrsi (Int Ref pH volt)   = 1
     Vrsi Std                 = 1
F -- pH Vrsi Calc             = 1
G -- Vrse (Ext Ref pH volt)   = 1
     Vrse Std                 = 1
H -- pH Vrse Calc             = 1
I -- Vrsi Biased (7pH=0v)     = 1
J -- Vrsi Biased Std          = 1
K -- Vrse Biased (7pH=0v)     = 1
L -- Vrse Biased Std          = 1

M -- Counter Electrode Vk     = 1
N -- Counter Electrode Vk Std = 1
O -- Counter Electrode Cur Ik = 1
P -- Substrate Current        = 1

Q -- Battery Volt             = 1
R -- Bias Battery Pos         = 1
S -- Bias Battery Neg         = 1
T -- Board Temperature        = 1
U -- Board Humidity           = 1

U -- Optode                   = 1
V -- Conductivity             = 1


 SampNum             MM/DD/YYYY HH:MM:SS          Vtherm        VthrmStd         TC_Dfet            Vrsi        Vrsi_std        pHintEst            Vrse        Vrse_std        pHextEst          Vrsi_B        VrsiBstd          Vrse_B VrseBstd              Vk          Vk_std              Ik              Ib        VbatMain        VbiasPos        VbiasNeg         TC_cont        Humidity                Opt_PN  Opt_SN  Moxy    O2satper        TC_opt  Dphase   Bphase  Rphase  Bamp    Bpot    Ramp    Opt_rawtemp     TempC   Cond    Salt    Unk1    Date    Time            Cond_PN Cond_SN Cond    Temp    Unk1    Unk2    Unk3

Pump         = 2002,   2002
Pwr on, init =   23,   2025
CTD Optode   =   22,   2047
Bias Bat     =   70,  2117
Ib Substrate =   61,   2178
Ik Cntr E    =   61,   2239
Vk Cntr E    =  989,   3228
Vtherm       =  250,   3478
Vrsi         =  508,   3986
Vrse         =  507,   4493
Vrse_B       =  508,   5001
Vrsi_B       =  508,   5509
Onchip ADC   =    0,   5509
Calc TempC   =    1,   5510
Calc pH Ext  =    1,   5511
Calc pH Int  =    1,   5512
Optode       =    0,   5512
CTD          =    0,   5512
format data  =    2,   5514
#0000491             05/17/2021 04:21:05        0.997609        0.000002          24.701        -0.055199       0.000011          18.183        -0.799234       0.000007           7.826        0.674919        0.000005        -0.069120        0.000007        -0.500557       0.000005        18197.77        -399.623           11.99            3.61           -3.60           17.50              60        4831    856     273.940 92.968  18.159  28.090  30.381   38.298  7.917   599.9   704.4   252.7   5860    29      -0.004  17.101  0.010   998.766 1473.130        (5514 msec)

uSD writes   =   14,   5528
wait TX      =   35,   5563





Pump         = 1900
Pwr on, init = 1920
CTD Optode   = 1920
Bias Bat     = 1920
Ib Substrate = 1980
Ik Cntr E    = 2040
Vk Cntr E    = 2300
Vtherm       = 2300
Vrsi         = 2300
Vrse         = 2810
Vrse_B       = 2810
Vrsi_B       = 2810
Onchip ADC   = 2810
Calc TempC   = 2810
Calc pH Ext  = 2810
Calc pH Int  = 2810
Optode       = 2810
CTD          = 2810
format data  = 2810
 SampNum             MM/DD/YYYY HH:MM:SS            Vrse        Vrse_std              Vk          Vk_std              Ik              Ib        VbatMain
#0000003             05/15/2021 09:36:42        -0.799235       0.000009        -0.500556       0.000003        18197.32        -399.593           12.00        (2810 msec)

uSD writes   = 2980
wait TX      = 2980





#endif

