#ifdef MERGELATER
/*
 * ec.c
 * Combines ADV and pH_back code for integrated EC measurements
 *
 *  Created on: Mar 9, 2022
 *      Author: Irene Hu
 */

#include "ec.h"

#define TIMESTAMP_FMT_FILE "%Y\t%m\t%d\t%H\t%M\t%S"

#define BUFFERTIME_MIN 0.2 // buffer around EC bursts to enable optode, etc

unsigned char prnBuf[SIZEOF_PRNBUF+4];      // size is defd in system.h - this copied straight from sample.c
// need a print buffer here for the summary file in burst mode

extern struct optodeDriver optode;
extern uint32_t     adc_data[];

//******************************************************************************
//* Menu
//******************************************************************************

void ec_comm_test(void)
// a test menu for EC comms test
// adapted from ph and adv test menus
{
    uprintf("\nWelcome to the EC comms test menu!  hurrah!\n");
    uprintf("\nCTD port is configured for pH and will not be compatible w/ CTD again until restart.\n");
    // init is called in test.c, which calls this menu

    int retVal;
    time_t timer;
    time_t currentTime;
    struct tm *time_struct;
    char input;
    char buff[256];

    // for 1 and 6 (measure)
    double totCtTime_min;
    char foldername[MAX_FILENAME];
    int charCount;
    // for 1 only
    int numBursts;

    // for ADV
    int mode;

    adv_open();

    while(1)
    {
        uprintf("\n\n\nmFET-mpHOX Comms Menu:\n");
        uprintf("1 -- EC measurements, burst mode\n");
        uprintf("3 -- Power on front-end pH sensor\n");
        uprintf("4 -- Power off front-end pH sensor\n");
        uprintf("5 -- Wake up ADV\n");
        uprintf("6 -- EC measurements\n");
        uprintf("7 -- Exit ADV measurement mode (error case)\n");
        uprintf("8 -- Stop continuous pH measurement (error case)\n");
        uprintf("9 -- Exit to Test Menu\n");
        uprintf("\nEnter Selection [9]: ");

        UARTFlushRx();
        timer = 30000 + ROM_HibernateRTCGet();  //30 second

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

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

        switch(input)
        {
            case '1':
            {   // EC in burst mode - for now, specify # bursts, not total duration

                while(1) {
                    uprintf("\nEnter measurement duration per burst (min): ");
                    if(getUserInput(buff, 20)) // assume this blocks until something is received?
                    {
                        if(sscanf(buff, "%lf", &totCtTime_min) == 1)
                        {
                            if(totCtTime_min <= 0)
                            {
                                uprintf("\nMeasurement duration must be greater than 0.\n");
                                continue;
                            }

                            break;
                        }
                        else // sscanf returned something besides 1
                        {
                            uprintf("\nPlease enter a single number.\n");
                            continue;
                        }
                    }
                    //else break; // no default value, so take this break out
                }

                while(1) {
                    uprintf("\nEnter total number of bursts: ");
                    if(getUserInput(buff, 20)) // assume this blocks until something is received?
                    {
                        if(sscanf(buff, "%d", &numBursts) == 1)
                        {
                            if(numBursts <= 0)
                            {
                                uprintf("\nNum bursts must be greater than 0.\n");
                                continue;
                            }

                            break;
                        }
                        else // sscanf returned something besides 1
                        {
                            uprintf("\nPlease enter a single number.\n");
                            continue;
                        }
                    }
                    //else break; // no default value, so take this break out
                }

                while(1) { // no default values, so keep asking until get a value

                    uprintf("\nEnter deployment/folder name (8 chars): ");
                    if( charCount = getUserInput(buff, MAX_FILENAME) )
                    {
                        if (charCount > 8) buff[8] = 0; // force it to 8
                        strcpy(foldername, buff);
                        uprintf("\nFolder name: %s\n", foldername);
                        break;
                    }
                }

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                uprintf("[%s] ", buff);
                // measure function will print the rest of the narration

                retVal = ecwrapper_burst(totCtTime_min, numBursts, foldername);

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                if (retVal == 1)
                    uprintf("\n[%s] Exited with errors.\n", buff);
                else
                    uprintf("\n[%s] Finished measuring and recording data.\n", buff);

                break;
            }


            case '3':
            {   // power on phf

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                uprintf("[%s] Powering on front-end sensor\n", buff);

                phb_openphf();
                break;
            }

            case '4':
            {   // power of phf

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                uprintf("[%s] Powering off front-end sensor\n", buff);

                phb_closephf();
                break;
            }

            case '5':
            {
                // Wake up ADV

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                adv_sendBreak();
                uprintf("[%s] Attempting to wake up ADV:\n", buff);

                ROM_SysCtlDelay(MILLISECOND*1000); // copied from optode code for a simple delay (other option is while loop using timer)

                adv_flushPort();

                break;
            }

            case '6':
            {
                // single EC measurement, no folder - to save variabls tho we reuse foldername

                while(1) {
                    uprintf("\nEnter measurement duration (min): ");
                    if(getUserInput(buff, 20)) // assume this blocks until something is received?
                    {
                        if(sscanf(buff, "%lf", &totCtTime_min) == 1)
                        {
                            if(totCtTime_min <= 0)
                            {
                                uprintf("\nMeasurement duration must be greater than 0.\n");
                                continue;
                            }

                            break;
                        }
                        else // sscanf returned something besides 1
                        {
                            uprintf("\nPlease enter a single number.\n");
                            continue;
                        }
                    }
                    //else break; // no default value, so take this break out
                }

                while(1) { // no default values, so keep asking until get a value

                    uprintf("\nEnter data file name (5 chars, no extension): ");
                    if( charCount = getUserInput(buff, MAX_FILENAME) )
                    {
                        if (charCount > 5) buff[5] = 0; // force it to 5
                        strcpy(foldername, buff);
                        uprintf("\nFile append sequence: %s\n", foldername);
                        break;
                    }
                }

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                uprintf("[%s] ", buff);
                // measure function will print the rest of the narration

                retVal = ecwrapper_noburst(totCtTime_min, foldername);

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                if (retVal == 1)
                    uprintf("\n[%s] Exited with errors.\n", buff);
                else
                    uprintf("\n[%s] Finished measuring and recording data.\n", buff);


                break;
            }

            case '7':
            {
                // exit ADV measurement mode - shouldn't need to call this unless things go wrong
                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                // int mode; - declared it up above, for now

                uprintf("[%s] Trying to exit ADV's measurement mode...\n", buff);
                if(adv_exitMeasMode() != 0) {
                    // adv_getMode(buff, &mode);
                    // uprintf("Error trying to exit measurement mode. Current mode is %s.\n", buff);
                    uprintf("Error trying to exit measurement mode. ");
                } else {
                    uprintf("Exited measurement mode. ");
                }
                // want to test this get mode function
                adv_getMode(buff, &mode);
                uprintf("Current mode is %s.\n", buff);

                break;

            }

            case '8':
            {   // stop pH continuous measurement - for an error case

                _phb_flushRx(); // error in comms can cause data to come from phf that wasn't processed

                currentTime = ROM_HibernateRTCGet();
                time_struct = localtime(&currentTime);
                strftime(buff, sizeof(buff),TIMESTAMP_FMT, time_struct);

                uprintf("[%s] Sending command to front-end sensor to stop continuous measurement\n", buff);

                retVal = phb_stopMeas();
                if (retVal == 1)
                    uprintf("\n[%s] Failed.\n", buff);
                else
                    uprintf("\n[%s] Ack received.\n", buff);
                break;
            }


             default: // Exit
                uprintf("\nPress enter to bring up main menu\n");
                return;

        } // end switch
    } // end while for menu printing
}


//******************************************************************************
//* EC measure function
//******************************************************************************


int ecwrapper_burst(double measDuration_min, int numbursts, char *foldername)
// wrapper for EC function in burst mode
// pass in measuring duration for each measurement and number of bursts
// if figure out a way to communicate live, can have it start immediately and stop on command
// for now, build in a 10 min lead time before turning pump on, and a time for it to end (via # bursts) so pump is off at retrieval
{

    char filename[MAX_FILENAME+9]; // gave folder name a max size of 8, plus the slash
    FIL reused_ofs; // file objects
    FRESULT fresult;
    int i, optodeState, bw;
    uint32_t indx = 0;

    time_t currentTime;
    struct tm *time_struct;

    // for aligning to hour
    // based on compute_nextWakeUp(void) in main.c
    uint32_t sampPeriod_s;
    time_t nextTime;

    // polling for cancel
    char inChar;
    int stopFlg = 0;

    // for env variables and battery monitoring
    float fVbat_main, fHumidity, fBrdTempC;
    uint32_t  uiHumidity, uiWater, tick_onchip;
    int j;

    if (sys_data.app_cfg[APPCFG_MICROSD_ENABLE] != 1)   // copied from Thom fix 6 Aug 2021
    {
        uprintf("Some lil flag is not set properly and the microsd is not enabled. This fxn relies on writing to files. Goodbye, better luck next time.\n");
        return 1;
    } // rather than only opening files etc if condition is met, just quit now if it isn't

    fresult = f_mkdir(foldername);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("Error making folder: %s\n", StringFromFresult(fresult));
        return 1;
    }

    // set up hour alignment
    // for now (3/22/22) we reuse this variable that was meant for normal deploy mode
    if (sys_data.sample_aligned) {
        sampPeriod_s = (uint32_t)(measDuration_min*60) % 3600; // align to fractions of hour - typecast minutes*60 to integer, truncating any fractions of s should be ok (C is pretty good with floating point errors?)

        // if aligned to hour, subtract some time to account for processing before/after EC measurement
        // enables user to enter round numbers
        measDuration_min = measDuration_min - BUFFERTIME_MIN;
    }

    uprintf("Initiating ADV+pH measurements for %d bursts of %.2f minutes each (total %.2f hours) at %d Hz\n", numbursts, measDuration_min, numbursts*measDuration_min/60, 512/_adv_config.AvgInterval);
    if (512/_adv_config.AvgInterval >= PH_ADCFREQ_VRS) { // for now, set ADC polling at 20 Hz.  it can't go faster than that
        uprintf("Warning: frequency %d Hz is faster than pH ADC frequency %d Hz; measurements will duplicate.\n", 512/_adv_config.AvgInterval, PH_ADCFREQ_VRS);
    }
    if (sys_data.sample_aligned) {
        uprintf("Align to hour is enabled; samples will be aligned to multiples of %" PRIu32 " s after each hour.\n", sampPeriod_s);
        if (sampPeriod_s == 0) sampPeriod_s = 3600; // seek next time in increments of 60 min, not 0 min
    }

    uprintf("Files will be in folder %s and named summary.txt, uc.txt, [dh / pc / sd / vd]_xxxxx.txt for summary, user config, data header, probe check, system data, and velocity+conc data, respectively.\n", foldername);
    // TODO: roughly estimate time needed for all this, subtract it etc so that the meas duration the person mentions includes all this and e.g. 1 hour meas duration will align all samples
    // also, align to hour sampling

    // user config - open, output, and close
    sprintf(filename, "%s/uc.txt", foldername);
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printUserConfig(&reused_ofs,0);

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    // set up summary file
    sprintf(filename, "%s/summary.txt", foldername);
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    // write heading for summary file
    sd_fprintf(&reused_ofs, "#\tYear\tMonth\tDay\tHour\tMinute\tSecond\t");
    // battery and env monitoring
    sd_fprintf(&reused_ofs, "Board TC\tHumidity\tBattery\t");
    // optode fields - copied from config.c print_write_data_header (trace thru test menu item D)
    sd_fprintf(&reused_ofs, "Opt_PN\tOpt_SN\tMoxy\tO2satper\tTC_opt\tDphase\tBphase\tRphase\tBamp\tBpot\tRamp\tOpt_rawtemp\n");

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    // for each burst
    for (i = 0; i < numbursts; i++) {

        // if aligned to hour, wait until an aligned time point
        if (sys_data.sample_aligned) {
            currentTime = ROM_HibernateRTCGet();
            nextTime = (currentTime / 3600) * 3600;         // Find last hour

            // THOM TODO: 2 Jul ??? this code needs an error check to insure time is not whacked - have seen a hang here
            // IH do we still have this error?

            while (nextTime < currentTime)  {// switched from do-while loop so it does NOT always add one sampling period, in case current time is exactly the start time
                nextTime += sampPeriod_s;
            }

            // kill time until next cycle
            while (ROM_HibernateRTCGet() < nextTime) {
                if(UARTRxBytesAvail())
                {
                    inChar = UARTgetc();
                    if(inChar == CTRL_X)
                    {
                        uprintf("Ctrl-x received, aborting EC trial.\n");
                        return 0;
                    }
                }
            }
        }

        // 7/20/22 check battery and env variables, and shut down if battery low - much copied from sample.c
        // turn on sensor and battery monitoring circuit - needs at least 150msec to settle
        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(10*MILLISECOND);

        adc_onchip_init();
        // 100msec recommended for battery voltage to settle to within 1%
        tick_onchip = Timer_1ms(false) ;

        // start constructing string to write
        indx = 0;
        indx += sprintf(&prnBuf[indx], "%d\t", i);
        currentTime = ROM_HibernateRTCGet();
        time_struct = localtime(&currentTime);
        indx += strftime(&prnBuf[indx], sizeof(prnBuf)-indx,TIMESTAMP_FMT_FILE, time_struct);

        // back to battery / env monitoring
        while(Timer_1ms(false) - tick_onchip <= 150) {      // at least 150msec for proper vbat reading
            ROM_SysCtlDelay(MILLISECOND * 5);
        }

        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

        // LM60 temp
        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
        // 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;

        indx += sprintf(&prnBuf[indx], "\t%.2f\t%u\t%.2f", fBrdTempC, uiHumidity, fVbat_main);


        // Check for low battery voltage. Exit and sleep if too low.
        if( fVbat_main < (sys_data.low_batt_volt + 0.005) )
        {
            for(j=0; j<5; j++)
            {
                /* save below for debugging - otherwise not super useful for EC
                if(j < 4)
                    uprintf("\nLow battery voltage (%.2f < %.2f) !\n", fVbat_main, sys_data.low_batt_volt );
                else
                    uprintf("\nLow battery voltage (%.2f < %.2f) ! Exiting deploy mode...\n", fVbat_main, sys_data.low_batt_volt );
                wait_consoleTx();
                */

                ROM_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
                fVbat_main = (float)(adc_data[0]*100);
                fVbat_main /= 18193.77;

                // write it to the string, same field as other battery
                indx += sprintf(&prnBuf[indx], ",%.2f", fVbat_main);

                // if the input voltage is above the low batt threshold, then go ahead with the sample.   If after 5 checks, then abort to prevent uSD card failure
                if(fVbat_main > sys_data.low_batt_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
                {
                    stopFlg = 1;
                }

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

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

            led_green_off();

        } // Low voltage handler
        // if stop flag was set, wait until finish measuring and writing optode, then we will exit gracefully

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

        // start pump - optode will be in a pumped flowcell
#if BOARD_MPHOX >= 1  || BOARD_MSC == 1
        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();
#endif

        //start the optode driver by passing 1, when complete, the statemachine returns 0
        optodeState = 1;

        while(optodeState > 0) { // 0 when done
            optodeState = optode_stateMachine(optodeState);
        }

        sprintf(filename, "%s/summary.txt", foldername);
        fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
        if(fresult != FR_OK)
        {
            // != FR_OK
            uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
            //error_store("f_open error");
        }
        else  // FILE is open
        {
            // Seek to the end, to append our file
            fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
            if(fresult != FR_OK)
            {
                uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
            }
        }

        // continue constructing string to write
        if (optode.state == -1) { // it timed out
            uprintf("Error reading optode for burst %d.\n",i);
            indx += sprintf(&prnBuf[indx], "\n");
        } else {
            indx += sprintf(&prnBuf[indx], "\t%s\n",optode.buf);
        }

        // write the assembled print buffer into the file
        fresult = f_write( &reused_ofs, prnBuf, strlen(prnBuf), (UINT*)&bw);
        if(fresult != FR_OK)
        {
            uprintf("f_write error: %s\n", StringFromFresult(fresult));
            // StringFromFresult is in microsd.c
        }

        fresult = f_close( &reused_ofs );
        if(fresult != FR_OK)
        {
         uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
         //error_store("f_close error");
        }

        // reuse the filename variable for this
        sprintf(filename,"%05d",i);

        // need to wake up ADV for the first one, if there's been a lag
        if (i == 0) {
            adv_sendBreak();
            uprintf("Attempting to wake up ADV:\n");

            ROM_SysCtlDelay(MILLISECOND*1000); // copied from optode code for a simple delay (other option is while loop using timer)

            adv_flushPort();
        }

        // just in case
        UARTFlushRx();

        // now call the ec function
        if (stopFlg == 0) {
            if ( ec(measDuration_min, foldername, filename) != 0) {
                uprintf("Error with EC burst %d.\n",i);
            }
        }

#if BOARD_MPHOX >= 1  || BOARD_MSC == 1
        pump_off();
        if(sys_data.test_mode == 1) uprintf("Pump off\n");
        led_red_off();
#endif

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

    }

    return 0;
}


int ecwrapper_noburst(double measDuration_min, char *fn_append)
// wrapper to make EC function do what it used to do before i modified it for burst mode compatibility
// intermediary step towards burst mode, but no folders
// don't clutter menu / save stack space, put in a function here
{

    char filename[MAX_FILENAME+9]; // gave folder name a max size of 8, plus the slash
    FIL reused_ofs; // file objects
    FRESULT fresult;

    if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] != 1)   // copied from Thom fix 6 Aug 2021
    {
        uprintf("Some lil flag is not set properly and the microsd is not enabled. This fxn relies on writing to files. Goodbye, better luck next time.\n");
        return 1;
    } // rather than only opening files etc if condition is met, just quit now if it isn't

    uprintf("Initiating ADV+pH measurements for %.2f minutes at %d Hz\n", measDuration_min, 512/_adv_config.AvgInterval);
    if (512/_adv_config.AvgInterval >= PH_ADCFREQ_VRS) { // for now, set ADC polling at 20 Hz.  it can't go faster than that
        uprintf("Warning: frequency %d Hz is faster than pH ADC frequency %d Hz; measurements will duplicate.\n", 512/_adv_config.AvgInterval, PH_ADCFREQ_VRS);
    }
    uprintf("Files will be named [uc / dh / pc / sd / vd]_%s.txt for user config, data header, probe check, system data, and velocity+conc data, respectively.\n", fn_append);

    // user config - open, output, and close
    sprintf(filename, "uc_%s.txt", fn_append);
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printUserConfig(&reused_ofs,0);

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    // now call the ec function
    return ec(measDuration_min, NULL, fn_append);
}


int ec(double measDuration_min, char* foldername, char* fn_append)
// measure ADV and pH for measDuration_min minutes at whatever frequency is set in ADV settings
// foldername is a character array containing foldername (folder should be already made) - set NULL to skip folder
// fn_append is a character array containing a <=5-char append string sequence (eg numbers)
// combines adv_measure and phb_measCont
// no option to output to screen; must write to file
// ph measurements are not timed; rather, ask for and receive one every time a velocity measurement is received
// ph data will show up in velocity data file
// for printing of timing data, works best if print_adv_timing is defined (adv.h) but not print_ph_timing (ph_base.h)
// 3/10/22 summary/warning text and user config output removed + restructure file naming, for better compatibility with burst mode
// return 0 if success, 1 if not
{
    uint64_t measDuration_ms = (uint64_t)(measDuration_min * 60 * 1000); // ms (not us) to use timers

    // not sure the below still matters
    /*
    // check that continuous measurement is enabled
    if (_adv_config.B1 != 0) {
        cerr << "Vector is in burst, not continuous mode. Download a new deployment file.\n";
        return 1;
    } */

    struct adv_dataHeader datHdrStruct;
    struct adv_probeChkData prbChkStruct;
    struct adv_sysData sysDatStruct;
    struct adv_velocityData velDatStruct;
    struct pH_ptdata pHDatStruct;
    pHDatStruct.sync = PH_SYNCBYTE; // set for checksum calc

    char filename[MAX_FILENAME+9]; // gave folder name a max size of 8, plus the slash
    FIL reused_ofs, sysdat_ofs, veldat_ofs; // file objects
    FRESULT fresult;

    // these are for velocity and sys data
    int16_t checkSum;
    int ID;

    // keep track of how many - now that I'm not counting pulses
    int numsys = 0, numvel = 0, numvelerrs = 0, numph = 0, numpherrs = 0;

    // flag for ph data ok
    int phvalid = 0;

    // polling for cancel
    int inChar;

    // check for microsd enable done by calling function
    // check for existence of folder (should be fine bc created by calling function)
    //sprintf(filename, "%s/", foldername); // i wonder if this is the problem with invalid name for foldername (need directory slash)
    if (foldername) {
        fresult = f_stat(foldername, NULL);
        if(fresult != FR_OK)
        {
            uprintf("Error with folder %s: %s\n", foldername, StringFromFresult(fresult));
            return 1;
        }
    }

    // set pH going first, so it doesn't mess with ADV timing at the beginning
    // since pH is polled, but ADV just streams
    _phb_flushRx(); // got an error once at the below...
    if (phb_startMeas() == 1) {
        uprintf("Failed to start pH measurement; exiting.\n");
        return 1;
    }

    //uprintf("\npH measurements kicked off.\n");

    // back to ADV
    // start recording - then will need 1 second before header data is available
    char cmd[] = "ST";
    //char cmd[] = "SD"; // 5/15/18 test if it's still streaming to serial
    if (_adv_sendTwoCharCmd(cmd) == 1) return 1;

    if (_adv_receiveAck() != 0) return 1;

    // 11/16/21 no longer need this since we're not implementing pauses during startup
    // Timer_1ms(TRUE); // reset 1ms tick - assumes timer not being used for anything else, otherwise need to store value here and calc a difference later

    // need to wait 1 s; while we wait, output user config and file headers
    //FA_OPEN_APPEND is not supported in this fatfs version

    // print user config to file - removed

    // system and velocity data - these need to stay open

    if (foldername) {
        sprintf(filename, "%s/sd_%s.txt", foldername, fn_append);
    } else {
        sprintf(filename, "sd_%s.txt", fn_append);
    }
    fresult = f_open(&sysdat_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // fatal if can't open files
        return 1;
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&sysdat_ofs, sysdat_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    if (foldername) {
        sprintf(filename, "%s/vd_%s.txt", foldername, fn_append);
    } else {
        sprintf(filename, "vd_%s.txt", fn_append);
    }
    fresult = f_open(&veldat_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // fatal if can't open files
        return 1;
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&veldat_ofs, veldat_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printSysDataHdr(&sysdat_ofs);
    adv_printVelDataHdr_wSNR(&veldat_ofs);

    // add pH header to end of velocity header, in that file
    sd_fprintf(&veldat_ofs, "\t");
    ph_printpHDataHdr(&veldat_ofs, 0);

    sd_fprintf(&sysdat_ofs, "\n");
    sd_fprintf(&veldat_ofs, "\n");

    // 1 s after sent command, can get header and probe check data
    //while ( Timer_1ms(FALSE) < 1000) ; // waiting loop
    // 11/16/21 this is now removed as it seems to cause timing problems, and is not necessary since functions will wait for data until it appears

    if (adv_getDatHeader(&datHdrStruct) == 1) return 1;
    if (adv_getProbeChkDat(&prbChkStruct) == 1) return 1;

    // need to wait another s; while we wait, output
    // data header files and probe check can open, write, and close now

    if (foldername) {
        sprintf(filename, "%s/dh_%s.txt", foldername, fn_append);
    } else {
        sprintf(filename, "dh_%s.txt", fn_append);
    }
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printDatHeader(&datHdrStruct, &reused_ofs);

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    if (foldername) {
        sprintf(filename, "%s/pc_%s.txt", foldername, fn_append);
    } else {
        sprintf(filename, "pc_%s.txt", fn_append);
    }
    fresult = f_open(&reused_ofs, filename, FA_READ | FA_WRITE | FA_OPEN_ALWAYS);
    if(fresult != FR_OK)
    {
        // != FR_OK
        uprintf("File %s f_open error: %s\n", filename, StringFromFresult(fresult));
        //error_store("f_open error");
        // how to handle this error condition?
    }
    else  // FILE is open
    {
        // Seek to the end, to append our file
        fresult = f_lseek(&reused_ofs, reused_ofs.fsize);
        if(fresult != FR_OK)
        {
            uprintf("File %s f_lseek error: %s\n", filename, StringFromFresult(fresult));
        }
    }

    adv_printProbeChkDat(&prbChkStruct, &reused_ofs);

    fresult = f_close( &reused_ofs );
    if(fresult != FR_OK)
    {
     uprintf("File %s f_close error: %s\n", filename, StringFromFresult(fresult));
     //error_store("f_close error");
    }

    // 2 s after command, can get system data
    //while ( Timer_1ms(FALSE) < 2000) ; // waiting loop
    // 11/16/21 this is now removed as it seems to cause timing problems, and is not necessary since functions will wait for data until it appears

    // system data requires a little more processing
    // check we're getting the right structure
    ID = _adv_sortIncomingDataStruct(&checkSum);
    if (ID == -1) return 1; // means transmission or sync byte failed- error msg already printed
    if (ID != ADV_IDSYS) {
        uprintf("Did not receive correct structure ID. Got %#x\n", ID);
        return 1;
    }
    if (adv_getSysData(&sysDatStruct, checkSum) == 1) return 1;
    numsys++;

    // without synch pulses, Vector will just start measuring
    // we'll estimate this as the starting time, so reset timer here
    Timer_1ms(TRUE);

    // output sys data
#ifdef PRINT_ADV_TIMING
    sd_fprintf(&sysdat_ofs, "0\t");
#endif
    adv_printSysData(&sysDatStruct, &sysdat_ofs);
    sd_fprintf(&sysdat_ofs, "\n");

    // sit and wait for data
    // error condition: do not abort if something goes wrong
    // TODO: rethink how to handle errors, when pH data stream is also coming in

    // for debugging
    uprintf("\nStarting to measure.\n");

    while( Timer_1ms(FALSE) < measDuration_ms)
    {
        // check for velocity and system data
        ID = _adv_sortIncomingDataStruct(&checkSum);
        // 7/21/22: ADV power failure behavior would be that above function continually times out
        // no data gets written to file and exits loop once measurement period is over

        switch (ID) {
            case -1: // transmission or synch byte failed
            {
                break;
            }

            case ADV_IDSYS: //system data
            {
                // other code has somethign to throw out incomplete data structures - ??

                if (adv_getSysData(&sysDatStruct, checkSum) == 1) {
                    uprintf("Failed to get system data %d; continuing to check for more data\n", numsys++);
                    break;
                }
#ifdef PRINT_ADV_TIMING
                sd_fprintf(&sysdat_ofs, "%.4f\t",(double)Timer_1ms(FALSE)/1000);
#endif
                adv_printSysData(&sysDatStruct, &sysdat_ofs);
                sd_fprintf(&sysdat_ofs, "\n");
                numsys++;

                // since we're not coordinating synch pulses, ok to go back to top of loop and check again for velocity
                // rather than having to read the velocity immediately

                break;

            }

            case ADV_IDVEL: //velocity data
            {
                // other code has somethign to throw out incomplete data structures - ??

                if (adv_getVelData(&velDatStruct, checkSum) == 1) {
                    uprintf("Failed to get velocity data %d; continuing to check for more data\n", numvel++);
                    break;
                }

                while (velDatStruct.Count != numvel % 256) {
                    uprintf("Velocity data point #%d not read in, read in point #%d.\n", numvel, (int)(velDatStruct.Count));
                    numvel++;
                    numvelerrs++;
                } // hopefully this loop does not happen or go out of control
                // because numvel is now being used as the main counter
                // if we missed one measurement, this loop is handy

                // get ph data here, then print them both out (for better time synching, since printing takes time)
                _phb_flushRx(); // bad previous measurement seems to leave some characters in the buffer
                if (phb_getMeas(&pHDatStruct) == 1) {
                    uprintf("Failed to get pH data %d; waiting for next point\n", numvel);
                    numpherrs++;
                } else { // got the measurement
                    phvalid = 1;
                }
                // 7/21/22: mFET power failure behavior would be that above function times out and returns 1
                // ph is marked invalid and does not print

#ifdef PRINT_ADV_TIMING
                sd_fprintf(&veldat_ofs, "%.4f\t",(double)Timer_1ms(FALSE)/1000);
#endif
                adv_printVelData_wSNR(&velDatStruct, &datHdrStruct, &veldat_ofs, numvel); // it starts at 0

                if (phvalid) { // have ph data

                    sd_fprintf(&veldat_ofs, "\t");

#ifdef PRINT_PH_TIMING
                    sd_fprintf(&veldat_ofs, "%.4f\t",(double)Timer_1ms(FALSE)/1000);
#endif
                    ph_printpHData(&pHDatStruct, &veldat_ofs, 0);

                }

                sd_fprintf(&veldat_ofs, "\n");

                phvalid = 0; // reset

                numph++;
                numvel++;

                break;
            }

            default:
            {
                uprintf("Got synch byte, but ID byte received (%#x) does not correspond to system or velocity data. Ignoring these bytes, continuing to check for more data.\n", ID);
                break;
            }
        } // end switch

        // poll for a ctrl-X to quit - copied partly from optode code
        if(UARTRxBytesAvail())
        {
            inChar = UARTgetc();
            if(inChar == CTRL_X)
            {
                uprintf("Ctrl-x received, aborting measurement.\n");
                break;
            }
        }

        // go back to top of loop, keep checking for data until timeout

    }

    // all done measuring!
    unsigned long elapsedTime = Timer_1ms(FALSE);

    if (phb_stopMeas() == 1) {
        uprintf("Error stopping measurement.\n"); // but keep going to close file etc
    }

    uprintf("Total %d system data, %d velocity data, and %d pH data received, plus %d velocity and %d pH readin errors, over a period of %.2f min\n", numsys, numvel-numvelerrs, numph-numpherrs, numvelerrs, numpherrs, (double)(elapsedTime)/60000);

    // close files
    fresult = f_close( &sysdat_ofs );
    if(fresult != FR_OK)
    {
        uprintf("System data f_close error: %s\n", StringFromFresult(fresult));
        //error_store("f_close error");
    }

    fresult = f_close( &veldat_ofs );
    if(fresult != FR_OK)
    {
        uprintf("Velocity data f_close error: %s\n", StringFromFresult(fresult));
        //error_store("f_close error");
    }

    return adv_exitMeasModeNoNarration();
}
#endif
