/*
 * David Muller; Germán Alfaro
 * davehmuller@gmail.com; alfaro.germanevera@gmail.com
 *
 * 2/2014 Robert Glatts, rglatts@ucsd.edu
 *
 * 4/2018 Thom Maughan, tm@mbari.org
 */


/*
 * System.c contains a variety of functions related to sensors and basic system utilities.
 */

#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_microcat.h"
#include "optode.h"
#include "sleep.h"
#include "microsd.h"
#include "fatfs/src/ff.h"
#include "uartstdio.h"	// User local version with larger RX buffer
#include "pwm.h"
#include "i2c_fram.h"
#include "config.h"


extern struct systemSamplingData sys_samp;

extern int microSD_flg;

extern int optode_stateMachine(int optodeState);

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

void fram_retrieve(void);

//void get_header(void);

//void print_data_header(void);

//int write_metadata_header(const time_t currentTime);
//void print_metadata_header(const time_t currentTime);




// Global variables
//extern char uart3_buff[];       // Optode
extern struct optodeDriver optode;
extern struct microCatDriver microCat;
extern struct ctdDriver ctd;   // struct defd in ctd_base.h


uint32_t sys_data_chksum32;

extern float fBiasPos;  // used for read_bias_bat();
extern float fBiasNeg;

//#define SIZEOF_SAMPBUF  160                 // Thom TODO BUG due to max data config sampBuf size
//char sampBuf[SIZEOF_SAMPBUF+4];     // for sl command


extern unsigned char prnBuf[SIZEOF_PRNBUF+4];      // size is defd in system.h

unsigned long timer_10ms = 0;       // ulong is uint32_t on this processor
unsigned long timer_1ms = 0;


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



#define TIME_TXSAMPLE_MSEC      30      // was 50

//V=I*R => V=I*15K => I=V/15K amps => I=V*10^9/15K nano-Amps => I=V*66.6k nA => 15uV/nA.
//#define COUNTERELECTRODE_IK_SCALE       66666.667   // 10^9/15K MCAP has 15K resistor, Ik
//
//#define SUBSTRATECURRENT_IB_SCALING     1000        // 10^9/1M = 1000   MCAP has 1M resistor for Ib, substrate current





/*****************************************************************************
*
* System tick interrupt handler.  Updated to 1ms for system timing.
* Requires: Requires ROM_SysTickPeriodSet(ROM_SysCtlClockGet() / 1000); in init.c
* Also, provides 10 ms tick for various timing requirements.
* FatFs requires a timer tick every 10 ms for internal timing purposes.
*
*****************************************************************************/
void SysTickHandler(void)
{
    timer_1ms++;
    if((timer_1ms % 10) == 0)
    {
        disk_timerproc();	// FatFs
        timer_10ms++;			// For Timer_10ms()
    }

    pwmSoln_stateMachine(-1);         // manages PG1,PG3 GPIO outputs,  -1 says use global state variables
    pwmPump_stateMachine(-1);
}

/****************************************************************************
 * Timer_10ms()
 *
 * Returns value of 10 ms system tick variable. Can be used for general
 * purpose timing where a simple delay is not workable.
 * Call with "reset" true to reset timer to zero.
 * RCG 4/14
 ***************************************************************************/
unsigned long Timer_10ms(bool reset)
{
	if(reset) timer_10ms = 0L;
	return timer_10ms;
}

/****************************************************************************
 * Timer_1ms()
 *
 * Returns value of 1 ms system tick variable. General purpose timing

 ***************************************************************************/
unsigned long Timer_1ms(bool reset)
{
    if(reset) timer_1ms = 0L;
    return timer_1ms;
}

uint32_t compute_sys_data_chksum(void)
{
    int indx;
    uint8_t *bytePtr;
    uint32_t chkSum32 = 0;

    bytePtr = (uint8_t *)&sys_data;

    for(indx=0; indx<sizeof(sys_data); indx++)
    {
        chkSum32 += *bytePtr;     // add byte at a time, sum into 32 bit checksum (later use a more bulletproof alg)
        bytePtr++;
    }
    return(chkSum32);
}


void initSysDataVolatileVariables(void)
{
    uprintf("\nWarning: Initialized volatile sys data variables\n");

    sys_samp.state = IDLE;
    sys_samp.nextWakeUp = ROM_HibernateRTCGet()+sys_data.sampling_period;
    sys_samp.next_gdata_fptr = 0L;      // deprecated (not used, need to confirm)
    sys_samp.current_sample = 0;
    sys_samp.AD24_std = 0.0;
    //sys_samp.deploy_time = 0;     // TODO

}




/*
 * Retrieves the saved sys_data variables from their storage
 * location in EEPROM (when the device hibernates, sys_data variables
 * are stored in EEPROM if there is a change - 500K write cycle limit).
 * If no value has been assigned to a sys_data variable, a default
 * value is assigned.
 */
void retrieveSysDataVariables()
{
	uint32_t ulStatus;

	//enable the EEPROM
	ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EEPROM0);

	ulStatus = EEPROMInit();
	if( ulStatus != EEPROM_INIT_OK)
	{
		uprintf("EEPROM Initialization error.\n");
	}

	//read the sys_data struct out of EEPROM.  Last argument ensures # of bytes is multiple of 4
	EEPROMRead((uint32_t*) &sys_data, 0x400, (sizeof(sys_data) + 3) & ~3);

    retrieveSysSampFRAMVariables();  // sys_samp

}



void write_nonvol_ram(void)         // system.c
{
    //int indx;

    // Need to enable the Hibernation peripheral after wake/reset, before using it.
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_HIBERNATE);

        // EnableExpClk should always be called, even if the module was already enabled,
        // because this function also initializes some timing parameters.
    ROM_HibernateEnableExpClk(ROM_SysCtlClockGet());

    // store volatile config data
    HibernateDataSet((uint32_t *)&sys_data_chg, SYSDATACHG_NUMWORDS);
    //uprintf("\nwrite_nonvol_ram() st=%u, nwup=%u, ngfptr=%u, cursam=%u, std=%u\n");
}

void read_nonvol_ram(void)         // system.c
{
    // Need to enable the Hibernation peripheral after wake/reset, before using it.
    ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_HIBERNATE);

        // EnableExpClk should always be called, even if the module was already enabled,
        // because this function also initializes some timing parameters.
    ROM_HibernateEnableExpClk(ROM_SysCtlClockGet());

    HibernateDataGet((uint32_t *)&sys_data_chg, SYSDATACHG_NUMWORDS);     // last parm is number of 4byte words

    //uprintf("\nread_nonvol_ram() st=%u, nwup=%u, ngfptr=%u, cursam=%u, std=%u\n");
}

uint32_t storeSysDataVariables(void)
{

    storeSysSampFRAMVariables();

    // store the sys_data structure (_vol is used to prevent changes in eeprom - this needs to be improved when we drop support for older hardware)
    // returns 0 on success
    return(EEPROMProgram((uint32_t*) &sys_data, 0x400, (sizeof(sys_data) + 3) & ~3));
}

void initSysSampFRAMVariables(void)
{

    sys_samp.state = IDLE;                  //430 IDLE, DEPLOYED, COMMAND,  NOT USER MODIFIABLE                        <<<<<------
    sys_samp.nextWakeUp = 0;            //434 Next intended RTC match (wake-up time) NOT USER MODIFIABLE           <<<<<------
    sys_samp.next_gdata_fptr = 0;       //438 The file position of the next gdata sample. NOT USER MODIFIABLE      <<<<<------
    sys_samp.current_sample = 0;        //442 The current data record to be stored. NOT USER MODIFIABLE            <<<<<------
    sys_samp.AD24_std = 0.0;                 //464 standard deviation for AD24                                          <<<<--------
    sys_samp.initNum= 0x55AA55AA;
    fram_sys_samp_store();

}


void storeSysSampFRAMVariables(void)
{

    if(sys_samp.initNum != 0x55AA55AA)
    {
        uprintf("FRAM sys_samp.initNum is not 0x55AA55AA\n");
        //initSysSampFRAMVariables();
        sys_samp.initNum= 0x55AA55AA;
    }


    fram_sys_samp_store();  // defd in i2c_fram.c

}

void retrieveSysSampFRAMVariables(void)
{


    fram_sys_samp_retrieve();  // defd in i2c_fram.c

    if(sys_samp.initNum != 0x55AA55AA)
    {
        uprintf("FRAM sys_samp.initNum is not 0x55AA55AA\n");
        initSysSampFRAMVariables();
        sys_samp.initNum= 0x55AA55AA;
    }


}

void initSysDataVariables(void)
{
    config_sys_data_default_init();     // defd in config.c
}





/*
 * 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()
{
	float batt_volt;

#if BOARD_MFET >= 1 || BOARD_MPHOX == 1
	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()
{
	float ADC_volts, batt_volt;

#if BOARD_MFET == 1
	batt_volt = 0.0;
#endif

#if BOARD_MFET == 2  || BOARD_MPHOX == 1
	// GPIO3 on ADS1248
	//    float is gnd
	//    0 = bias-
	//    1 = bias+
	ADS1248_gpio3(1);
	ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);
	batt_volt = ADC_volts*2.0;
#endif
	return(batt_volt);
}

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


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

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

    ADS1248_gpio3(1);
    ROM_SysCtlDelay(MILLISECOND * 10);
    ADC_volts = pollADS1248_iso(7, 6, 5, 1, 20);    // throw away first reading
    ROM_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\n", biasneg, biasplus);
    //ROM_SysCtlDelay(MILLISECOND * 500);


    closeADS1248_iso();
}

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();
    ROM_SysCtlDelay(MILLISECOND * 100);
    ADS1248_gpio_init();

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

        ADS1248_gpio3(1);
        ROM_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\n", biasneg, biasplus);
        ROM_SysCtlDelay(MILLISECOND * 500);
    }

    closeADS1248_iso();
}

/*
 * temp_int()
 * Returns internal temperature
 * RCG 4/14
 */
float controller_temp()
{
	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
// This function applies only to the MFET
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
    ROM_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, 0xFF);   // Turn on VBat monitoring

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

    adc_onchip_init();

    // delay to allow voltage divider circuit to charge up
    ROM_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\n");

    return(fVbat);
 }
#endif






// Calculates temperature from Durafet's thermistor. V_therm is the voltage from thermistor, measured using isolated 24-bit ADC.
// TCOffset is an user input, to correct thermistor to be more accurate.
float DuraFET_temp(float V_therm, float TCOffset)
{
	// Declare variables
    float TC;
	double Rtherm;
	double TC4, TC4ln, TC3;
	double c0, c1, c2, c3;
	double A, B, C;
	double dVtherm;

	dVtherm = (double)V_therm;

#ifdef NOCODE
	float c0 = 340.9819863;
	float c1 = -9.10257E-05;
	float c2 = -95.08806667;
	float c3 = 0.965370274;
#endif

	//if(quadratic)
	// Thermistor coefficients, Steinhart-Hart coefficients
    c0 = sys_data.quadTherm_c0;      //340.9819863;
    c1 = sys_data.quadTherm_c1;      //-9.10257E-05;
    c2 = sys_data.quadTherm_c2;      //-95.08806667;
    c3 = sys_data.quadTherm_c3;      //0.965370274;

    //A = sys_data.shTherm_A;
    //B = sys_data.shTherm_B;
    //C = sys_data.shTherm_C;


    A = sys_data.quadTherm_c0;
    B = sys_data.quadTherm_c2;
    C = sys_data.quadTherm_c3;

#define TEMP_ABS    273.15
	// Calculate TC
	Rtherm = 20000 / ((3.3 / dVtherm) - 1);

	//This code came from Scripps
	TC4 = c0 + c1*Rtherm + c2*(log10(Rtherm)) + c3*(pow(log10(Rtherm), 3));   // I suspect conversion to Celsius is built into c0 coefficient.

	TC4ln = c0 + c1*Rtherm + c2*(log(Rtherm)) + c3*(pow(log(Rtherm), 3));

	//1/T = A + B(LnR) + C(LnR)^3  (Kelvin) - This Thom has coded from an AVX appnote on NTC thermistors, plus some other research
	TC3 = (double)1 / (double)( A + B*(log(Rtherm)) + C*(pow(log(Rtherm), 3)) );
	//TC3 -= TEMP_ABS;  // convert to Celsius

	// Apply offset to TC; TCOffset is an user input,
	// or derived during pH calibration process.
	TC = 0.0;
    if(sys_data.app_cfg[APPCFG_TEMPC_SH_ALG] == 0)
        TC = (float)TC4;
	if(sys_data.app_cfg[APPCFG_TEMPC_SH_ALG] == 1)
	    TC = (float)TC3;

//uprintf("\nDEBUG TC4 = %8.4lf, TC4ln = %8.4lf,  TC3 = %8.4lf\n;", TC4, TC4ln, TC3);

	TC = TC + TCOffset;

	//Rtherm =20000./(3.3./Vtherm-1);
	//
	//Steinhart - Hart Equation 1/T = A + B(LnR) + C(LnR)^3  (Kelvin)


	return TC;
}

/*
* Calculate pH using external reference.
* Eext refers to the voltage from the external reference, measured using isolated 24-bit ADC.
* TC should be calculated using fucntion getDurafettemp.
* Salt refers to salinity, extracted from SBE 37.
* RCG 5/14
*/
float calc_pHext(float Eext, float TC, float salt)
{
	float TK;				// Temperature in Kelvin
	float S_T;				// Nernst Slope
	float Eoext;			// Calibration coefficient corrected to in situ temperature
	float Z; 				// Ionic Strength; Dickson 2007
	float SO4_tot; 			// Total sulfate concentration; Dickson 2007
	float mCl; 				// Molality of Chloride; Dickson 2007;
	float K_HSO4; 			// Bisulfate acidity constant; Dickson 1990.
	float DHconst;			// Debye-Huckel; Khoo et al. 1997
	float log10gamma_HCl;	// log10 of mean activity coefficient of HCl in seawater
	float pHext_free;		// pH on free scale calculated from external reference
	float pHext_tot;		// pH on total scale calculated from external reference

	// Start calculations
	TK = TC + 273.15;
	S_T = (R_ * TK) / F_ * LN10;
	//Eoext = sys_data.Eo_ext_25C + dEoEXT_dt * (TC - 25);
	Eoext = sys_data.k0ext + dEoEXT_dt * (TC - 25);

	Z = 19.924 * salt / (1000 - 1.005 * salt); // Ionic strength
	SO4_tot = (0.14 / 96.062) * (salt / 1.80655); // molality sulfate from conservative relationship.
	mCl = 0.99889 / 35.453 * salt / 1.80655; // moallity chloride from conservative relationship.
	K_HSO4 =  exp(-4276.1/(TK)+141.328-23.093*log(TK)+(-13856/(TK)+324.57-47.986*log(TK))*sqrt(Z)+(35474/(TK)-771.54+114.723*log(TK))*Z-2698/(TK)*pow(sqrt(Z), 3)+1776/(TK)*pow(Z, 2)+log(1-0.001005*salt));
	DHconst = 0.00000343 * (pow(TC, 2)) + 0.00067524 * TC + 0.49172143; //Debye-Huckel
	log10gamma_HCl = 2*(-DHconst * sqrt(Z) / (1 + 1.394 * sqrt(Z)) + (0.08885 - 0.000111 * TC) * Z);

	pHext_free =  -( (Eoext - Eext) - S_T * (log10(mCl) + log10gamma_HCl) ) / S_T;
	pHext_tot = pHext_free - log10(1 + SO4_tot / K_HSO4); // pH from external reference electrode on total scale.
	return pHext_tot;

}

/*
* calc_pHint()
* Function to calculate pH using internal reference.
* Eint refers to the voltage from the internal reference, measured using the isolated 24-bit ADC.
* TC should be calculated using function getDurafettemp.
* RCG 5/14
*/
float calc_pHint(float Eint, float TC)
{
	float TK; 		// Temperature in Kelvin
	float S_T; 		// Nernest Slope
	float Eoint;  	// Calibration coefficient corrected to in situ temperature.
	float pHint;

	TK = TC + 273.15;
	S_T = (R_ * TK) / F_ * LN10;

	//Eoint = sys_data.Eo_int_25C + dEoINT_dt * (TC - 25);
	Eoint = sys_data.k0int + dEoINT_dt * (TC - 25);

	pHint = (Eint - Eoint) / S_T;

	return pHint;
}

/*
 * ph_calib()
 * Calculates DuraFET pH sensorcalibration values
 * using either user entered values, or automatic
 * calibration using temperature and salinity derived
 * from a MicroCAT CTD.
 * RCG 5/14
 */
void ph_calib(void)
{
	int response, i, j, samp, lines;
	char buff[100];
	static float Vint=0, Vext=0, pHcalpt=0, tempC, salt;
	float ftemp, tempK, E0int, S_T, Z, SO4_tot, mCl, K_HSO4, pHint_free, dummy, Vtherm, TCOffset;
	float DHconst, log10gamma_HCl, mHfree, aHfree_aCl, E0ext, sqrt_Z, E0int25, E0ext25;	// beta_SO4,
	float StdDev[SD_VALUES][SD_DIFF_SQ+1], diff, sum;
	float Vint_SD, Vext_SD, Vtherm_SD, tempC_SD, salt_SD;

	float   conductivity, density, speedSound;
	unsigned int model, sernum;
	unsigned char charInput;


	uprintf("\nph_calib() not supported in this version\n");
	//return;

	// pH Calibration point
	while(1)
	{
		sprintf(buff, "\n\nEnter pH calibration point [%.3f]: ", pHcalpt);
		uprintf("%s", buff);
		if(getUserInput(buff, strlen(buff)))
		{
			if(sscanf(buff, "%f", &ftemp) == 1)
			{
				if(ftemp >= 0 && ftemp <= 14)
				{
					pHcalpt = ftemp;
					uprintf("pH cal pt = %.3f\n", pHcalpt);
					break;
				}
				else uprintf("\nEnter between 0 and 14");
				continue;
			}
			else
			{
			    uprintf("DEBUG: sscanf fails to return 1");
			    break;   // if sscanf fails, exit?
			}
		}
		else
		{
		    uprintf("DEBUG: getUserInput fails to return 1");
		    break;         // if
		}
	}

	// Automatic or manual calibration?
	response = yesOrNoMenuChoice("\n\nPerform automatic DuraFET calibration (Y/N) [N]? ", NO);

	// Manual calibration
	if(response == NO)
	{
		// Vint
		while(1)
		{
			sprintf(buff, "\n\nEnter Vrsi [%f]: ", Vint);
			uprintf("%s", buff);
			if(getUserInput(buff, strlen(buff)))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= (-2) && ftemp <= 2)
					{
						Vint = ftemp;
						break;
					}
					else uprintf("Enter number between -2 and 2\n\n");
					continue;
				}
				else break;
			}
			else break;
		}

		// Vext
		while(1)
		{
			sprintf(buff, "\n\nEnter Vrse [%f]: ", Vext);
			uprintf("%s", buff);
			if(getUserInput(buff, strlen(buff)))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= (-2) && ftemp <= 2)
					{
						Vext = ftemp;
						break;
					}
					else uprintf("Enter number between -2 and 2\n\n");
					continue;
				}
				else break;
			}
			else break;
		}

		// Temperature
		while(1)
		{
			sprintf(buff, "\nEnter bath temperature [%f]: ", tempC);
			uprintf("%s", buff);
			if(getUserInput(buff, strlen(buff)))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= 0 && ftemp <= 100)
					{
						tempC = ftemp;
						break;
					}
					else uprintf("\nEnter between 0 and 100");
					continue;
				}
				else break;
			}
			else break;
		}

		// Salinity
		while(1)
		{
			sprintf(buff, "\nEnter bath salinity [%f]: ", salt);
			uprintf("%s", buff);
			if(getUserInput(buff, strlen(buff)))
			{
				if(sscanf(buff, "%f", &ftemp) == 1)
				{
					if(ftemp >= 0 && ftemp <= 100)
					{
						salt = ftemp;
						break;
					}
					else uprintf("\nEnter between 0 and 100");
					continue;
				}
				else break;
			}
			else break;
		}
	}

	// Automatic calibration
	else
	{
		// Clear standard deviation array
		for(j=0; j<SD_AVG; j++)
		{
			for(i=0; i<5; i++) StdDev[i][j] = 0;
		}

		j = 0;				// Reset standard deviation array counter
		lines = 20; 		// Force program to write column labels before first data line

		//ctd_open();
		openADS1248_iso();
		ROM_SysCtlDelay(ONESEC);	// Let instruments and sensors settle
		uprintf("\n\nPlace DuraFET and MicroCAT in calibration bath.");
		uprintf("\nHit any key when values have stabilized...");
		UARTFlushRx();

		while(1)
		{
			Vint = pollADS1248_iso(1, 6, sys_data.Vrsi_trials, 1, 5);
			Vext = pollADS1248_iso(4, 6, sys_data.Vrse_trials, 1, 5);
			Vtherm = pollADS1248_iso(0, 6, sys_data.Vtherm_trials, 1, 5);	// Chan 0-6, trials = user, gain = 1, sps = 5 Hz
			tempC = DuraFET_temp(Vtherm, sys_data.TCOffset);   // Thom, uncommented this line, future, allow entry of TCOffset as difference between MicroCat temp

			int ctdState;
			ctdState = 1;
			//uprintf("DEBUG: CTD statemachine = 1\n");
			while(1)
	        {
			    // ctd will open and close in the loop, might want to mod statemachine for ctd always on operation
	            if(ctdState == 0)            // makes sure ctd statemachine is finished
	                break;                  //ctd.salt and ctd.tempC returned on success
	            ctdState = ctd_stateMachine(ctdState);

                if(UARTRxBytesAvail())
                {
                    charInput = UARTgetc();

                    if(charInput == CTRL_X)  //24
                    {
                        ctd.tempC = 0.0;
                        ctd.salt = 0.0;
                        ctd_close();
                        //uprintf("Debug: reading of ctd terminated by ctrl-x\n");
                        break;
                    }

                }

	            // allow user to exit with Ctrl-X

	        }

#ifdef OLDCODE
			ctd_microCat_poll();        //pollMicroCAT();
			ROM_SysCtlDelay(5*ONESEC);	// Wait for instruments to respond
			ctd_microCat_parseData();	// Extract the data from response strings
#endif

			//uprintf("Debug:  tempC = %f, salt = %f", ctd.tempC, ctd.salt);
			tempC = ctd.tempC;
			salt = ctd.salt;

			// Store current sample values
			if(samp < SD_AVG) samp++;
			else samp = 0;
			StdDev[0][samp] = Vint;
			StdDev[1][samp] = Vext;
			StdDev[2][samp] = Vtherm;
			StdDev[3][samp] = tempC;
			StdDev[4][samp] = salt;


			// Find the mean of the last SD_AVG samples for each sensor
			for(i=0; i<SD_VALUES; i++)
			{
				sum = 0;

				for(j=0; j<SD_AVG; j++)
				{
					sum += StdDev[i][j];
				}

				StdDev[i][SD_MEAN] = sum / SD_AVG;	// Current mean for each sensor
			}

			// Find the difference squared for each of the last SD_AVG samples
			// Then find the sum of the squared differences
			for(i=0; i<SD_VALUES; i++)
			{
				sum = 0;

				for(j=0; j<SD_AVG; j++)
				{
					 diff = (StdDev[i][j] - StdDev[i][SD_MEAN]) ;	// Difference for each sample
					 sum += diff * diff;							// Sum of differences squared
				}

				StdDev[i][SD_DIFF_SQ] = sum;
			}

			// Take square root for each sensor
			Vint_SD = sqrt(StdDev[0][SD_DIFF_SQ]);
			Vext_SD = sqrt(StdDev[1][SD_DIFF_SQ]);
			Vtherm_SD = sqrt(StdDev[2][SD_DIFF_SQ]);
			tempC_SD = sqrt(StdDev[3][SD_DIFF_SQ]);
			salt_SD = sqrt(StdDev[4][SD_DIFF_SQ]);

			// Write column headers every 20 lines
			if(lines >= 20)
			{
#ifdef OLDCODE
				uprintf("\n\nVint\t\tVint-SD\t"
						"Vext\t\tVext-SD\t"
						"Vtherm\tVtherm-SD\t"
						"Temp\t\tTemp-SD\t"
						"Salinity\tSal-SD");
#endif
                uprintf("\n\n    Vrsi\t Vrsi-SD\t"
                        "    Vrse\t Vrse-SD\t"
                        "  Vtherm\tVthermSD\t"
                        "   TempC\tTempC-SD\t"
                        "Salinity\tSal-SD");

				lines = 1;		// Reset line counter
			}
			else lines++;

			// Write data line
			uprintf("\n%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f\t"
					"%8.5f\t%8.5f",
					Vint, Vint_SD,
					Vext, Vext_SD,
					Vtherm, Vtherm_SD,
					tempC, tempC_SD,
					salt, salt_SD);

			// Key hit, so display current averaged values
			if(kbhit())
			{
				uprintf("\n\nAverage of last %d values", SD_AVG);

				Vint = StdDev[0][SD_MEAN];
				uprintf("\nVint = %f V  SD = %f", Vint, Vint_SD);

				Vext = StdDev[1][SD_MEAN];
				uprintf("\nVext = %f V  SD = %f", Vext, Vext_SD);

				Vtherm = StdDev[2][SD_MEAN];
				uprintf("\nVtherm = %f C  SD = %f", Vtherm, Vtherm_SD);

				tempC = StdDev[3][SD_MEAN];
				uprintf("\nMicroCAT Temperature = %f C  SD = %f", tempC, tempC_SD);

				salt = StdDev[4][SD_MEAN];
				uprintf("\nMicroCAT Salinity = %f  SD = %f", salt, salt_SD);

				response = yesOrNoMenuChoice("\n\nValues acceptable (Y/N) [N]? ", NO);
				if(response == NO) continue;
				else break;
			}
		}

		closeADS1248_iso();
		ctd_close(); 	//closeMicroCAT();
	}

	// Calculate E0int25 and E0ext25 using either manual or automatic values
	tempK = tempC + 273.15;  					// Convert temp from C to K
	S_T = (R_ * tempK) / F_ * log(10); 			// Nernst temp dependence
	E0int = Vint - S_T * pHcalpt; 				// Calc E0int from Nernst & pH @ calibration point
	E0int25 = E0int + dEoINT_dt * (25 - tempC);

	Z = 19.924 * salt / (1000 - 1.005 * salt); 			// Ionic strength, Dickson et al. 2007
	sqrt_Z = sqrt(Z);

	SO4_tot = (0.14 / 96.062) * (salt / 1.80655);  	// Total conservative sulfate
	mCl = 0.99889/35.453*salt/1.80655; 				// Conservative chloride

	// Bisulfate equilibrium const., Dickson et al. 2007
	K_HSO4 = exp( -4276.1 / tempK + 141.328 - 23.093 * log(tempK) \
	          + ( -13856 / tempK + 324.57 - 47.986 * log(tempK) ) * sqrt_Z \
	          + ( 35474 / tempK - 771.54 + 114.723 * log(tempK) ) * Z - 2698 / tempK * pow(Z, 1.5) \
	          + 1776 / tempK * Z * Z + log(1 - 0.001005 * salt) );

	pHint_free = pHcalpt + log10( 1 + SO4_tot / K_HSO4);
	DHconst = 0.00000343 * tempC * tempC + 0.00067524 * tempC + 0.49172143; //Debye-Huckel, Khoo et al. 1977
	log10gamma_HCl = 2 * (-DHconst * sqrt_Z / ( 1 + 1.394 * sqrt_Z ) + (0.08885 - 0.000111 * tempC) * Z);
	mHfree = pow(10, (-pHint_free) );
	aHfree_aCl = mHfree * mCl * pow( 10, (log10gamma_HCl) );
	E0ext = Vext + S_T * log10(aHfree_aCl);
	E0ext25 = E0ext + dEoEXT_dt * (25-tempC);

	TCOffset =  tempC - DuraFET_temp(Vtherm, 0);

	uprintf("\n\nEo Int @ 25C = %f", E0int25);
	uprintf("\nEo Ext @ 25C = %f", E0ext25);
	uprintf("\nTCOffset = %f", sys_data.TCOffset);

	response = yesOrNoMenuChoice("\n\nAccept and store calibration values(Y/N) [N]? ", NO);
	if(response == YES)
	{
		//sys_data.Eo_int_25C = E0int25;
		sys_data.k0int = E0int25;
		
		//sys_data.Eo_ext_25C = E0ext25;
		sys_data.k0ext = E0ext25;			// variable rename 2 Apr 2021  Eo_ext_25C to k0ext

		sys_data.TCOffset = TCOffset;

		uprintf("\nCalibration values stored");
	}

}



/*
 *  send last sample, then take new sample and store to SD card
 * Take measurements and write them to SD card.
 */
void sendlast_takesample(void)
{
    FIL fileObject;
    FRESULT fresult;
#define MAX_DATASAMPSIZE    240     // this is oversized by over 20 bytes
    unsigned char buf[MAX_DATASAMPSIZE];
    unsigned int readSize;

    if(microSD_flg == 0)
    {
        uprintf("void sendlast_takesample(void) is not supported with microSD disabled\n");
        return;

    }

    uprintf("THOM TODO:  Sendlast_takesample is not done yet\n");

    // open snapshot.txt, send all until /n
    fresult = f_open(&fileObject, "snapshot.txt", FA_READ);  // Creates a new file. If the file is existing, it will be truncated and overwritten.
    if(fresult != FR_OK)
    {
        // file does not exist, first time reading so previous data not available
        //get_sample(0);      // arg=0 says 'dont tx the result', this will create the snapshot.txt file (after a delay)
        fast_sample(0);   // THOM 14 May 2021
        fresult = f_open(&fileObject, "snapshot.txt", FA_READ);  // Creates a new file. If the file is existing, it will be truncated and overwritten.
        if(fresult != FR_OK)
        {
            uprintf("f_open error snapshot.txt: %s\n", StringFromFresult(fresult));
            error_store("f_open error on snapshot.txt");
            return;
        }

    }

    fresult = f_read(&fileObject, &buf[0], MAX_DATASAMPSIZE-1, &readSize);
    if(fresult != FR_OK)
    {
        uprintf("f_read error snapshot.txt\n");
    }
    else
    {

        if(readSize > 4)
        {
            if(readSize >= MAX_DATASAMPSIZE)        // in the odd chance of an error
                readSize = MAX_DATASAMPSIZE-1;
            buf[readSize] = 0x00;       // null terminate

            uprintf("%s", buf);       //uprintf("%s\n", buf); print the sample

        }
        else
        {
            uprintf("bad read size (less than 5)\n");
        }
    }

    // Close the file
    fresult = f_close( &fileObject );
    if(fresult != FR_OK)
    {
        //uprintf("8\n");
        uprintf("f_close error snapshot.txt: %s\n", StringFromFresult(fresult));
        error_store("f_close error");
    }

    //get_sample(0);          // get sample for reading with next 'sl'
    fast_sample(0);
}



/****************************************************************
 * Get the current time according to the RTC.
 * returns in time-strcut
 ****************************************************************/
struct tm *get_RTC_time(void)
{
    //char timeStamp[TIMESTAMPLENGTH];
    uint32_t ticks;
    struct tm *time_struct;

    //get the time from the from the RTC so we can print it
    ticks = ROM_HibernateRTCGet();

    time_struct = localtime((const time_t*)&ticks);

    // DEBUG to print the time
    //strftime(timeStamp, sizeof(timeStamp),"%m/%d/%y %H:%M:%S", time_struct);
    //uprintf("%s", timeStamp);

    return(time_struct);
}




#ifdef MOVEDTOSAMPLEC


#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1 || BOARD_NANOFET >= 1
/*
 * fast sample()
 * Take measurements and write them to SD card.
 * sys_data.diag = 1 turns on timing diagnostics
 *
 */
void fast_sample(int txFlg)
{
    char timestamp[TIMESTAMPLENGTH];
    uint32_t timeout;
    uint32_t tickStart, ticks, tickDelta, tickEnd;
    struct tm *time_struct;

    float fVtherm;           // DuraFET raw temperature voltage
    float fVtherm_std;       // Std Dev for Durafet Thermistor
    float fTempC;            // Calculated DuraFET temperature
    //float fExtTemp;

    float fVrsi;             // DuraFET internal reference voltage   (Vint)
    float fVrsi_std;         // Std Dev for Durafet Internal Voltage
    float pHint;             // Calculated pH using internal reference

    float fVrse;             // External (AUX) reference voltage  (Vrs)
    float fVrse_std;         // Std Dev for Durafet External Voltage
    float pHext;             // Calculated pH using external reference

    float SBE_sal;           // MicroCAT salinity

    float fVrse_B;
    float fVrse_B_std;
    float fVrsi_B;
    float fVrsi_B_std;

    float fIk;              // (Ik) CounterElectrodeCurrent
    float fIk_std;
    float fVk;              // (Vk) CounterElectrodeVoltage
    float fVk_std;
    float fIb;              // (Ib) SubstrateCurrent
                            // float SubstrateVoltage;  //float SubstrateVoltage_std;
    float fVbat_main;
    float fBias_bat_lowest;

    float fHumidity;
    float fBrdTempC;
    uint32_t  uiHumidity;
    uint32_t  uiWater;

    uint32_t indx;
    int optodeState;
    int ctdState;

    FIL fileObject;
    FRESULT fresult;
    INT bw;

    tickStart = Timer_1ms(true);
    tickDelta = tickStart;
    char inChar;

    if(sys_data.test_mode == 1)
        uprintf("\nSampling...");
		
    //sys_data.app_cfg[APPCFG_SAMP_FLEX] = 1;   //FORCE use of the variables for trials and sps
    //sys_data.app_cfg[APPCFG_SAMPLING_DIAG] = 1;   //FORCE the timing data output

#if BOARD_MPHOX >= 1
	if(sys_data.pumpon_time > 0)
	{
	    // Pump on time
	    if(sys_data.test_mode == 1)
	        uprintf("\nPump on (test mode)\n");     // Don't want the pump to run in test deployment
	    else
	        pump_on();     // PORTD-3 is hanging in the breeze on MFET   ROM_GPIOPinWrite(GPIO_PORTD_BASE, GPIO_PIN_3, 0xff);
	    led_red_on();

	    timeout = Timer_1ms(false) + sys_data.pumpon_time*1000;

	    do
	    {
	        ROM_SysCtlDelay(100 * MILLISECOND);

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

	    //} while( ROM_HibernateRTCGet() < timeout );
	    } while( Timer_1ms(false) < timeout );

	    pump_off();
	    if(sys_data.test_mode == 1) uprintf("Pump off\n");
	    led_red_off();
	}
#endif

	led_green_on();

	tickDelta = Timer_1ms(false) - tickDelta;
    ticks = (Timer_1ms(false) - tickStart);
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nPump         = %5u,   %5u", tickDelta, ticks);
	tickDelta = ticks;

    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
    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();  //ROM_GPIOPinWrite(GPIO_PORTM_BASE, GPIO_PIN_6, 0xFF);   // Turn on VBat monitoring

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

    if(sys_data.data_cfg[CFG_OPTODE] > 0)  { optodeState = optode_stateMachine(optodeState); }
    if(sys_data.data_cfg[CFG_CTD] > 0)   { ctdState = ctd_stateMachine(ctdState);    }

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

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

    optodeState = 0;
    // Open and poll the microCat and Optode
    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        //start the optode driver by passing 1, when complete, the statemachine returns 0
        optodeState = 1;
        optodeState = optode_stateMachine(optodeState);     //pass in 1 to start the state machine, hereafter pass retVal

        tickDelta = Timer_1ms(false) - tickDelta;
        ticks = (Timer_1ms(false) - tickStart);
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode St8Ma = %5u,   %5u", tickDelta, ticks);
        tickDelta = ticks;
    }
    ctdState = 0;
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        //start the ctd driver by passing 1, when complete, the statemachine returns 0
        ctdState = 1;
        ctdState = ctd_stateMachine(ctdState);     //pass in 1 to start the state machine, hereafter pass retVal

        tickDelta = Timer_1ms(false) - tickDelta;
        ticks = (Timer_1ms(false) - tickStart);
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo  St8Ma = %5u,   %5u", tickDelta, ticks);
        tickDelta = ticks;
    }


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

    if(sys_data.data_cfg[CFG_BIAS_BAT_NEG] > 0)
    {
        ADS1248_gpio3(0);
        ROM_SysCtlDelay(MILLISECOND * 10);
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            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
        else
            fBiasNeg = pollADS1248_iso(7, 6, VBIAS_TRIALS, 1, VBIAS_SPS);     // Mux 01, Chan +7 -6, trials = 1, gain = 1, sps = 40 Hz
        fBiasNeg = fBiasNeg*2.0;                        // global var
    }

    if(sys_data.data_cfg[CFG_BIAS_BAT_POS] > 0)
    {
        ADS1248_gpio3(1);
        ROM_SysCtlDelay(MILLISECOND * 10);
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            fBiasPos = pollADS1248_iso(7, 6, sys_data.Vbias_trials, 1, sys_data.Vbias_sps);
        else
            fBiasPos = pollADS1248_iso(7, 6, VBIAS_TRIALS, 1, VBIAS_SPS);     // Mux 01, Chan +7 -6, trials = 1, gain = 1, sps = 40 Hz
        fBiasPos = fBiasPos*2.0;                        // global var
    }


    // report whichever battery voltage is lower (the minus supply or the plus supply)  //TODO, make separate measurements / config data fields
    fBias_bat_lowest = fBiasPos;
    if(fBiasPos > (-1*fBiasNeg))
        fBias_bat_lowest = fBiasNeg;

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

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }




    // ----------- Ib Subst Cur, (Diagnostic) ------------------
    if(sys_data.data_cfg[CFG_I_SUBSTRATE] > 0)
    {
        ADS1248_gpio_ADG1609_mux(2);
        ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            fIb = pollADS1248_iso(1, 6, sys_data.Ib_trials, 1, sys_data.Ib_sps);       // GLIDER 1 sample, 20 hz
        else
            fIb = pollADS1248_iso(1, 6, IB_TRIALS, 1, IB_SPS);       //  Mux 10, Chan +1 -6, trials = 1, gain = 1, sps = 20 Hz

        fIb *= SUBSTRATECURRENT_IB_SCALING;   // convert to nanoAmps (multiply by 1000)         // new 2 Apr 2021

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

    }

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }



    // ------------ Ik, CntrE Cur, (GLIDER)  Counter Electrode Current -------------
    if(sys_data.data_cfg[CFG_I_COUNTER] > 0)
    {
        ADS1248_gpio_ADG1609_mux(1);
        ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            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
        else
            fIk = pollADS1248_iso(1, 5, IK_TRIALS, 1, IK_SPS);  // Mux=01 Chan +5 -1, trials = 1, gain = 1, sps = 20 Hz  //

        // 15uV/nA for Ik - assuming Ik is counter electrode current.
        // V=IR  15uV = 1nA * R (15Kohm)
        fIk -= sys_data.offset_Ik;   // subtract opamp input offet voltage
        fIk *= COUNTERELECTRODE_IK_SCALE;

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

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

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }




    // Vk, Counter Electrode Voltage, CntrE Volt, (Diagnostic)
    if(sys_data.data_cfg[CFG_V_COUNTER_ELECT] > 0)
    {
        ADS1248_gpio_ADG1609_mux(1);
        ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            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
        else
            fVk = pollADS1248_iso(1, 6, VK_TRIALS, 1, VK_SPS);   // Mux 01, Chan +1 -6, trials = 5, gain = 1, sps = 20 Hz


        fVk_std = sys_samp.AD24_std;

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

    }

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }



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

    //------------ Vtherm, Thermistor Voltage ----------------
    if(sys_data.data_cfg[CFG_V_THERMISTOR] > 0)
    {
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            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)
        else
            fVtherm = pollADS1248_iso(0, 6, VTHERM_TRIALS, 1, VTHERM_SPS);       // Chan +0 -6, trials = 5, gain = 1, sps = 20 Hz (per Yui 5May2021)

        fVtherm_std = sys_samp.AD24_std;

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

    }

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }



    //-------------- 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
        //dbg_printf("\nno intref on glider\n");
        ADS1248_gpio_ADG1609_mux(3);
        ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            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
        else
            fVrsi_B = pollADS1248_iso(1, 5, VRSI_TRIALS, 1, 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
        tickDelta = Timer_1ms(false) - tickDelta;
        ticks = (Timer_1ms(false) - tickStart);
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nVrsi_B       = %5u,   %5u", tickDelta, ticks);
        tickDelta = ticks;
    }

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }




    // ----------- Vrsi (aka Vint) --------------
    if(sys_data.data_cfg[CFG_VRSI] > 0)        // Not valid on nanoFET
    {
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1
        //dbg_printf("\nno Vrsi on glider, not valid on nanoFET\n");
        ADS1248_gpio_ADG1609_mux(3);
        ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            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)
        else
            fVrsi = pollADS1248_iso(1, 6, VRSI_TRIALS, 1, VRSI_SPS);       // Chan +1 -6, trials = 10, gain = 1, sps = 20 Hz (10*50ms = 500ms)

        fVrsi_std = sys_samp.AD24_std;

#endif
#if BOARD_NANOFET >= 1
        fVrsi = 0.0;
        fVrsi_std = 0.0;
#endif

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

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }





    // --------- 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
        dbg_printf("\nno extref on glider\n");
        ADS1248_gpio_ADG1609_mux(0);
        ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            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
        else
            fVrse_B = pollADS1248_iso(1, 5, VRSE_TRIALS, 1, 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
        tickDelta = Timer_1ms(false) - tickDelta;
        ticks = (Timer_1ms(false) - tickStart);
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nVrse_B       = %5u,   %5u", tickDelta, ticks);
        tickDelta = ticks;
    }

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }





    // ----------- fVrse (GLIDER) ----------------------
    if(sys_data.data_cfg[CFG_VRSE] > 0)
    {
        ADS1248_gpio_ADG1609_mux(0);
        ROM_SysCtlDelay(MILLISECOND * 10);      // 10msec settling for MUX analog
        fVrse = 0.0;         // new 30 Mar 2021
#if BOARD_MFET >= 1 || BOARD_MPHOX >= 1     //6 (gnd) NOT available on nanoFET
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            fVrse = pollADS1248_iso(1, 6, sys_data.Vrse_trials, 1, sys_data.Vrse_sps);  // 10x50msec= 500msec  trials=10, gain=1, sps=20
        else
            fVrse = pollADS1248_iso(1, 6, VRSE_TRIALS, 1, VRSE_SPS);  // 10x50msec= 500msec  trials=10, gain=1, sps=20

#endif
#if BOARD_NANOFET >= 1
            // 6 (GND) is not connected on nanoFET
        if(sys_data.app_cfg[APPCFG_SAMP_FLEX] == 1)
            fVrse = pollADS1248_iso(1, 5, sys_data.Vrse_trials, 1, sys_data.Vrse_sps);  // 10x50msec= 500msec  trials=10, gain=1, sps=20
        else
            fVrse = pollADS1248_iso(1, 5, VRSE_TRIALS, 1, VRSE_SPS);  // 10x50msec= 500msec  trials=10, gain=1, sps=20
#endif

        fVrse_std = sys_samp.AD24_std;

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


    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }



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

    // battery voltage must be read for 'too low' check at bottom
    fVbat_main = (float)(adc_data[0]*100);
    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)
    {
        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
        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;

        fHumidity = (float)( (adc_data[2] * 100) );  // VOUT=(VSUPPLY)(0.00636(sensor RH) + 0.1515), typical at 25C
        fHumidity /= 3284;
        uiHumidity = (adc_data[2] * 100) / 3284;
    }

    tickDelta = Timer_1ms(false) - tickDelta;
    ticks = (Timer_1ms(false) - tickStart);
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOnchip ADC   = %5u,   %5u", tickDelta, ticks);
    tickDelta = ticks;

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

    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {
        optodeState = optode_stateMachine(optodeState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }
    if(sys_data.data_cfg[CFG_CTD] > 0)
    {
        ctdState = ctd_stateMachine(ctdState);
        if((Timer_1ms(false) - tickDelta) > 1)
        {
            tickDelta = Timer_1ms(false) - tickDelta;
            ticks = (Timer_1ms(false) - tickStart);
            if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCondo        = %5u,   %5u", tickDelta, ticks);
            tickDelta = ticks;
        }
    }




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

    if(sys_data.data_cfg[CFG_CALC_TEMP] > 0)
    {
        fTempC = DuraFET_temp(fVtherm, sys_data.TCOffset);

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



    if(sys_data.data_cfg[CFG_CALC_PH_VRSE] > 0)
    {
        SBE_sal = sys_data.default_sal;       // for pH_ext calc  (init'd to 35.0)
        // Todo sys_data.sensor_sal;
        pHext = calc_pHext(fVrse, fTempC, SBE_sal);  // NOTE: this might be Vrse_B that is needed....   5 May 2021

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



    if(sys_data.data_cfg[CFG_CALC_PH_VRSI] > 0)
    {
        // requires TC and fVrsi_B
        pHint = calc_pHint(fVrsi_B, fTempC);   // per YUI  7 Jun 2018

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




    if(sys_data.data_cfg[CFG_OPTODE] > 0)
    {

        while(1)
        {
            if(optodeState == 0)            // makes sure optode statemachine is finished
                break;
            optodeState = optode_stateMachine(optodeState);

        }
        tickDelta = Timer_1ms(false) - tickDelta;
        ticks = (Timer_1ms(false) - tickStart);
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nOptode       = %5u,   %5u", tickDelta, ticks);
        tickDelta = ticks;
    }



    if(sys_data.data_cfg[CFG_CTD] > 0)
    {

        while(1)
        {
            if(ctdState == 0)            // makes sure ctd statemachine is finished
                break;
            ctdState = ctd_stateMachine(ctdState);

        }
        tickDelta = Timer_1ms(false) - tickDelta;
        ticks = (Timer_1ms(false) - tickStart);
        if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nCTD          = %5u,   %5u", tickDelta, ticks);
        tickDelta = ticks;
    }



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

    if(sys_data.data_cfg[CFG_SAMPLE_NUM] > 0)       { indx += sprintf(&prnBuf[indx], "#%07u\t", sys_samp.current_sample);   } //0  // was %07d
    if(sys_data.data_cfg[CFG_TIMEDATE] > 0)         { indx += sprintf(&prnBuf[indx], "     %s\t", timestamp);               } //1

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

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

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

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

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

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

    if(sys_data.data_cfg[CFG_OPTODE] > 0)           { indx += sprintf(&prnBuf[indx], "%s\t", optode.buf);                   } //23
    if(sys_data.data_cfg[CFG_CTD] > 0)              { indx += sprintf(&prnBuf[indx], "%s\t", ctd.buf);                      } //23

    tickDelta = Timer_1ms(false) - tickDelta;
    ticks = (Timer_1ms(false) - tickStart);
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nformat data  = %5u,   %5u\n", tickDelta, ticks);
    tickDelta = ticks;

    tickEnd = Timer_1ms(false);
    ticks = (tickEnd - tickStart);
    //uprintf("(Time: %u msec)", ticks );
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1)  indx += sprintf(&prnBuf[indx], "(%u msec)\t", ticks);
    indx += sprintf(&prnBuf[indx], "\r\n");  // should this be cr-lf?
    prnBuf[indx] = 0;       // new, trying to track down a bug where the string does not print all the way, might not be 0 term

    //if(txFlg == 1) uprintf("%s", prnBuf);      // uprint maximum is 255 sample to console
    if(txFlg == 1) UARTwrite(prnBuf, strlen(prnBuf));

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

        // Seek to the end, to append our file
        fresult = f_lseek(&fileObject, fileObject.fsize);
        if(fresult != FR_OK)
        {
            uprintf("f_lseek error: %s\n", StringFromFresult(fresult));
            error_store("f_lseek error");
        }

        // Write the formatted data string in the prnBuf buffer to the SD card
        fresult = f_write( &fileObject, prnBuf, strlen(prnBuf), (UINT*)&bw);
        if(fresult != FR_OK)
        {
            uprintf("f_write error: %s\n", StringFromFresult(fresult));
        }

        // Close the file
        fresult = f_close( &fileObject );
        if(fresult != FR_OK)
        {
            uprintf("f_close error: %s\n", StringFromFresult(fresult));
            error_store("f_close error");
        }
    } // if microSD_flg

    tickDelta = Timer_1ms(false) - tickDelta;
    ticks = (Timer_1ms(false) - tickStart);
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nuSD writes   = %5u,   %5u", tickDelta, ticks);
    tickDelta = ticks;

    wait_consoleTx();

    tickDelta = Timer_1ms(false) - tickDelta;
    ticks = (Timer_1ms(false) - tickStart);
    if(sys_data.app_cfg[APPCFG_SAMPLING_DIAG] == 1) uprintf("\nwait TX      = %5u,   %5u", tickDelta, ticks);
    tickDelta = ticks;

    sys_samp.current_sample++;  // Increment the sample counter, this is stored to FRAM in sleep() routine

    led_green_off();

    // Check for low battery voltage. Exit and sleep if too low.
    if( fVbat_main < sys_data.low_batt_volt )
    {

        for(indx=0; indx<20; indx++)
        {
            if(indx%4 == 0) uprintf("\nLow battery voltage (%.2f < %.2f) ! Exiting deploy mode...\n", fVbat_main, sys_data.low_batt_volt );
            wait_consoleTx();

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

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

        error_store("Low battery! Exiting deploy mode");
        sleep(IDLE, 0);     // Sleep until woken by user
    }

// .diag is gone

}

#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

#endif // moving to sample.c

/*
 * error_store()
 * Stores error messages with timestamp.
 * Appends a newline character to message.
 * Function is useful during deployment when console is disconnected
 * RCG 5/14
 */
void error_store(const char *error_buff)
{
	char timestamp[TIMESTAMPLENGTH];
	uint32_t ticks;
	struct tm *time_struct;
	FIL fil;		// File object
	FRESULT fresult;
	UINT bw;

	if(microSD_flg == 0)        // 20 Jan 2021 TGM
	{
	    uprintf("error_stor() not supported with microSD_flg=0 disabled\n");
	    return;
	}

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

	// Seek to the end, to append our file
	fresult = f_lseek(&fil, fil.fsize);
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_lseek error: %s\n", StringFromFresult(fresult));
	}

	// Write timestamp to file
	ticks = ROM_HibernateRTCGet();
	time_struct = localtime(&ticks);
	strftime(timestamp, sizeof(timestamp),"\n%Y/%m/%d %H:%M:%S ", time_struct);

	fresult = f_write( &fil, timestamp, strlen(timestamp), &bw );
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_write error: %s\n", StringFromFresult(fresult));
	}

	// Write error message to file
	fresult = f_write( &fil, error_buff, strlen(error_buff), &bw );
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_write error: %s\n", StringFromFresult(fresult));
	}

	// Close the file
	fresult = f_close( &fil );
	if(fresult != FR_OK)
	{
		uprintf("Error.txt f_close error: %s\n", StringFromFresult(fresult));
	}

}




void ms_delay(unsigned int msec)
{
    ROM_SysCtlDelay(MILLISECOND*msec);
}

void delay_msec(unsigned int msec)
{
    ROM_SysCtlDelay(MILLISECOND*msec);
}

//*****************************************************************************
//
// The error routine that is called if the driver library encounters an error.
//
//*****************************************************************************
#ifdef DEBUG
void
__error__(char *pcFilename, uint32_t ui32Line)
{
}
#endif



FRESULT open_append (
    FIL* fp,            /* [OUT] File object to create */
    const char* path    /* [IN]  File name to be opened */
)
{
    FRESULT fr;

    /* Opens an existing file. If not exist, creates a new file. */
    fr = f_open(fp, path, FA_WRITE | FA_OPEN_ALWAYS);
    if (fr == FR_OK) {
        /* Seek to end of the file to append data */
        fr = f_lseek(fp, f_size(fp));
        if (fr != FR_OK)
            f_close(fp);
    }
    return fr;
}




