/*
 * serial_cmd_fxn.c
 *
 *  Created on: May 3, 2024
 *      Author: thomm
 */
#include "serial_cmd_fxn.h"

#include "main.h"
#include "system.h"
#include "sleep.h"
#include "sample.h"
#include "serial.h"
#include "config.h"
#include "test.h"
#include "uart3.h"
#include "uartstdio.h"
#include "user_io.h"
#include "serial.h"
#include "board_util.h"
#include "apf.h"
#include "apf_util.h"
#include "apf_math.h"
#include "apf_init.h"
#include "apf_numbr.h"
#include "mms_spec.h"
#include "mms_test.h"
#include "sample.h"
#include "ymodem.h"
#include "zmodem.h"
#include "i2c_fram.h"
#include "apf_test.h"
#include "i2c_ina260.h"
#include "init_defaults.h"
#include "i2c_ds3231.h"
#include "i2c_bme280.h"

#include "stdint.h"
#include "string.h"
#include "stdbool.h"

extern struct rxStateMach rx0;             // defd in serial.h
extern struct watchDog wdog;                // defd in board_util.h

extern long   size, avail;      //defd in apfutil.c

extern unsigned int databarFlg;
extern unsigned int barfSampleTime;

extern const char* wisdom_arr[];
extern const char* contrib_arr[];
extern const char* ser_commands_arr[];
extern int mmsDbgFlg; //defd in mms_spec.c
extern uint32_t fram_apf_vars_retrievedFlg;
extern uint32_t fram_apfArray_retrievedFlg;

uint32_t serRxFlushAfterCmdFlg = 1;       //enabled in MoSoS v5.1.1, disabling serRxFlush may have been causing hangs in v5.1.0, not root caused 22 May 2025

void apf_apf_vars_debug(void)
{
    prn_nl();

    uprintf("fram_apf_vars_retrievedFlg = %u", fram_apf_vars_retrievedFlg); prn_nl();
    uprintf("fram_apfArray_retrievedFlg = %u", fram_apfArray_retrievedFlg); prn_nl();
    prn_nl();


    uprintf("apf.isus_tcomp_mode    = %u", apf_eeprom.isus_tcomp_mode); prn_nl();
    uprintf("apf.isus_salfit_mode   = %u", apf_eeprom.isus_salfit_mode); prn_nl();
    prn_nl();

    uprintf("apf.isus_pixel_beg     = %u", apf_eeprom.isus_pixel_beg); prn_nl();
    uprintf("apf.isus_pixel_end     = %u", apf_eeprom.isus_pixel_end); prn_nl();

    uprintf("apf_eeprom.lambda_beg         = %u", apf_eeprom.lambda_beg); prn_nl();
    uprintf("apf_eeprom.lambda_end         = %u", apf_eeprom.lambda_end); prn_nl();

    uprintf("apf_eeprom.isus_datpixel_beg  = %u", apf_eeprom.isus_datpixel_beg); prn_nl();
    uprintf("apf_eeprom.isus_datpixel_end  = %u", apf_eeprom.isus_datpixel_end); prn_nl();

    uprintf("apf_eeprom.lambda_dat_beg         = %u", apf_eeprom.lambda_dat_beg); prn_nl();
    uprintf("apf_eeprom.lambda_dat_end         = %u", apf_eeprom.lambda_dat_end); prn_nl();

    uprintf("apf.isus_fit_pixels    = %u", apf_eeprom.isus_fit_pixels); prn_nl();
    prn_nl();

    uprintf("apf.CTD_Time           = %u",   apf.CTD_Time); prn_nl();
    uprintf("apf.CTD_Temp           = %.5f", apf.CTD_Temp); prn_nl();
    uprintf("apf.CTD_Salinity       = %.5f", apf.CTD_Salinity); prn_nl();
    uprintf("apf.CTD_Depth          = %.5f", apf.CTD_Depth); prn_nl();
    prn_nl();

    uprintf("apf.isus_sal           = %.5f", apf.isus_sal); prn_nl();
    uprintf("apf.isus_no3           = %.5f", apf.isus_no3); prn_nl();
    uprintf("apf.isus_hs            = %.5f", apf.isus_hs); prn_nl();
    uprintf("apf.isus_err           = %.5f", apf.isus_err); prn_nl();

    prn_nl();

}




void apf_print_help(uint32_t mode)
{
    if(mode > 0)
    {
        prn_nl(); uprintf("APF Commands: W,M,E,P,S,T,C,Q,INIT,TINT,MODE,SS,S1,S2,SL,TS,T1,T2,SE,SLP,OFF,RTC,FIT,CTD,BAKE,SPECTRA,VER,PROFILE");  prn_nl();
        prn_nl(); uprintf("File Commands: ls, cd, pwd, cat, touch, cp, rm, mkdir, format, yr, ys"); prn_nl();
        prn_nl(); uprintf("pH Commands: time, vrse, vtherm, phext, ib, ik, vk, biaspos, biasneg, tempc, vrsi, phint");  prn_nl();
        prn_nl();
    }

    uprintf("term time = %u msec", apf_h2m_timeout_get()); prn_nl();
}


void print_cmd_big_help(void)
{
    uprintf("                 Ping:  W          ACK"); prn_nl();
    uprintf("                 Menu:  M          <menu launches>"); prn_nl();
    uprintf("         Error Counts:  E          ACK,E,3,3033"); prn_nl();
    uprintf("  PwrOn, SysRst, Evnt:  P          ACK,P,03,3937292061"); prn_nl();
    uprintf("       Sample Counter:  S          ACK"); prn_nl();
    uprintf("             Acq Time:  T          ACK"); prn_nl();
    uprintf("       Get MSC Config:  C          H, Datalogger Serial Num , SN:49024..."); prn_nl();
    uprintf("      Initialize Vars:  INIT       ACK,INIT"); prn_nl();
    uprintf("     Integration Time:  TINT,400   ACK,TINT,400"); prn_nl();
    uprintf("  Dura=2 Isus=1  Mode:  MODE,1     ACK,MODE,1"); prn_nl();
    uprintf("  Silent Sample Modal:  SS     "); prn_nl();
    uprintf("   Silent Isus Sample:  S1     "); prn_nl();
    uprintf("Silent pH Dura Sample:  S2     "); prn_nl();
    uprintf("    Take Sample Modal:  TS     "); prn_nl();
    uprintf("     Take Isus Sample:  T1     "); prn_nl();
    uprintf("  Take pH Dura Sample:  T2     "); prn_nl();
    wait_consoleTx();
    uprintf("     Send Last sample:  SL     "); prn_nl();
    uprintf("  Send MSC error msgs:  SE     "); prn_nl();
    uprintf("                Sleep:  SLP    "); prn_nl();
    uprintf(" Prepare for PowerOff:  OFF    "); prn_nl();
    uprintf("              Set RTC:  RTC,1727464426    "); prn_nl();
    uprintf("              Get RTC:  RTC,?  "); prn_nl();
    uprintf("                 Ping:  FIT    "); prn_nl();
    uprintf("              Set CTD:  CTD,1727464446,20.0000,35.0000,-5.00    "); prn_nl();
    uprintf("              Get CTD:  CTD,?    "); prn_nl();
    uprintf("             Bake,sec:  BAKE,10   ACK,BAKE,CMD,10"); prn_nl();
    uprintf("            Send last:  SL     "); prn_nl();
    uprintf("    Get MSC Error msg:  SE     "); prn_nl(); prn_nl();
    uprintf(" get (Query) MSC type:  Q          ACK,Q,4   Types: 1=NONE, 2=Isus, 3=Dura, 4=Isus+Dura"); prn_nl();
    uprintf("term time = %u msec", apf_h2m_timeout_get()); prn_nl();
    wait_consoleTx();
}


void print_cmd_help(void)
{

    uprintf("** datalogger setup:  0 for off, 1 for on"); prn_nl();
    uprintf(" time, gettime"); prn_nl();
    uprintf(" time MM/DD/YY HH:MM:SS, settime MM/DD/YY HH:MM:SS");  prn_nl();
    uprintf(" per, period, getperiod");  prn_nl();
    uprintf(" per 20, period 20, setperiod 20");  prn_nl();
    uprintf(" config, getconfig ");  prn_nl();
    uprintf(" config, setconfig");  prn_nl();
    uprintf(" config help");  prn_nl();
    uprintf(" filename, getfilename");  prn_nl();
    uprintf(" filename filename.ext, setfilename filename.ext");  prn_nl();
    uprintf(" microsd, store"); prn_nl();
    uprintf(" align");  prn_nl();
    uprintf("** commands:");  prn_nl();
    uprintf(" help");  prn_nl();
    uprintf(" sleep");  prn_nl();
    uprintf(" deploy");  prn_nl();
    uprintf(" ts");  prn_nl();
    uprintf(" ss");  prn_nl();
    uprintf(" sl");  prn_nl();
    uprintf(" gc");  prn_nl();
    uprintf(" ec");  prn_nl();
    uprintf(" gh");  prn_nl();
    uprintf(" eh");  prn_nl();
    uprintf(" stop, exit, quit, <ctrl-c>");  prn_nl();
    prn_nl();
    uprintf("vrsi set 1    vrsi get    vrsi    vrsi 0   vrsi 2000");


}

void cmd_help_all(void)
{
    uint32_t indx;

    indx=0;
    for(indx=0; indx<200; indx++)
    {
        if(ser_commands_arr[indx] == 0)
        {
            prn_nl();
            break;
        }
        uprintf("  %s", ser_commands_arr[indx]); prn_nl();
        wait_consoleTx();
    }

}


void print_cmd_file_help(void)
{
    /*
     *
     * help  : Display list of commands
        h  : Alias for help
        ?  : Alias for help
        cat  : Display contents of a text file
        cd  : Alias for chdir
        chdir  : Change directory
        chkdsk  : Check Disk
        cp  : Copy file
        format  : Format Disk
        ls  : Display list of files
        mkdir  : Make new directory
        mv  : Rename or move a file or directory
        pwd  : Display current working directory
        rm  : Remove a file or directory
        touch  : Create file or update file timestamp
        ys  : Send file via YMODEM
        yr  : Receive file via YMODEM
        exit  : Exit the file viewer
     *
     */
    uprintf("File commands:"); prn_nl(); prn_nl();

    uprintf(" cat    : Display contents of a text file"); prn_nl();
    uprintf(" cd     : Change directory"); prn_nl();
    uprintf(" cp     : Copy file"); prn_nl();
    uprintf(" ls     : Display list of files"); prn_nl();
    uprintf(" mkdir  : Make new directory"); prn_nl();
    uprintf(" mv     : Move a file or directory, aka, rename"); prn_nl();
    uprintf(" pwd    : Print working directory");  prn_nl();
    uprintf(" rm     : Remove a file or directory"); prn_nl();
    uprintf(" touch  : Create file or update file timestamp"); prn_nl();
    uprintf(" ys     : Send file via YMODEM"); prn_nl();
    uprintf(" yr     : Receive file via YMODEM"); prn_nl();

}


void apf_test_print_help(void)
{
    prn_nl();
    uprintf("test,h - this help on test command"); prn_nl();

    uprintf("test,array     diagnostic print apfArray from FRAM"); prn_nl();
    uprintf("test,array,d   EcoefsCnt destroyed to 255"); prn_nl();
    uprintf("test,array,r   EcoefsCnt restored to 256"); prn_nl();
    uprintf("test,dura      dura pH test"); prn_nl();
    uprintf("test,fram,1    size check"); prn_nl();
    uprintf("test,fram      soak test"); prn_nl();
    uprintf("test,file      microsd_fatfs_hammer_test(0)"); prn_nl();
    prn_nl();
    uprintf("test,error,<string>    insert a string in error.log  test,error,this is the story of a man named jed"); prn_nl();
    uprintf("test,ivt,<filename>    current and voltage test   test,ivt,sylvester.txt");  prn_nl();
    uprintf("test,probe     probe i2c bus"); prn_nl();
    uprintf("test,spec      test spectrometer"); prn_nl();
    uprintf("test,lamp      test lamp"); prn_nl();
    uprintf("test,log       test log"); prn_nl();
    uprintf("test,vars      print variables"); prn_nl();

}


/*
 * BAKE,NN command (NN in seconds 1 - 90)
 *    While BAKE is executing, it looks like no other command response will be received (in a delay_sec)
 */
int mms_bake_handler(uint32_t bakeTimeSec)
{
    unsigned int indx;
    unsigned char rxByte;
    int     abortFlg;
    float       fSysVolt, fSysAmp;
    float       fLampVolt, fLampAmp;
    float       fBrdTempC, fBrdPress;
    float       fTimeLeft;
    uint32_t    uiHumidity;
    char        ebuf[128];
    uint32_t    ticks, tickStart;

    //Check if voltage is below threshold
    //NEW, per the MSCInterfaceSpec, check if voltage is below ISUS_V_MIN, if low (spec was 8.0v, but that has been raised to 10v after a stir)
    ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
    if(fSysVolt < sys_data.isus_vin_min_volt)      //ISUS_SYS_V_MIN)
    {
        if(mmsDbgFlg==1) { uprintf("DBG: Input Voltage, %.2f v, is too low, aborting BAKE command.  Current = %.3f a", fSysVolt, fSysAmp);  prn_nl(); }
        mms_lampPwrOff();       //Lamp power should be off, not needed here, but just in case someone was fiddling with test code in thomzone

        sprintf(ebuf, "BAKE command aborted, Sys Vin: %.2f < %.2f, Iin: %.2f mA", fSysVolt, sys_data.isus_vin_min_volt, fSysAmp*1000);
        LogErrorMsg(ebuf, (int)LAMP_PWR_ON_ABORT_VINMIN, apf_eeprom.sys_message_enable);

        uprintf("NAK,BAKE,STAT"); prn_nl();

        return( ERROR );    //-1
    }

    mms_lampPwrOn();        //takes a couple seconds to power on the lamp (toggling W on and off, not sure why, but that's what was in MSC1, so it is written, so it shall be done)

    ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current

#define BAKE_FIBERLITE_MIN_CURRENT  0.250
    if(fSysAmp < BAKE_FIBERLITE_MIN_CURRENT)
    {
        sprintf(ebuf, "BAKE command aborted, Sys Iin: %.2f < %umA", fSysAmp, (uint32_t)(BAKE_FIBERLITE_MIN_CURRENT*1000));
        LogErrorMsg(ebuf, (int)LAMP_PWR_ON_ABORT_VINMIN, apf_eeprom.sys_message_enable);

        mms_lampPwrOff();       //Lamp power should be off, not needed here, but just in case someone was fiddling with test code in thomzone

        uprintf("NAK,BAKE,STAT"); prn_nl();
        return( ERROR );    //-1
    }

    uprintf("ACK,BAKE,STAT"); prn_nl();

    abortFlg = 0;
    tickStart = Timer_1ms();
    for(indx=0; indx<bakeTimeSec; indx++)
    {
        ina260_system_voltageCurrent( &fSysVolt, &fSysAmp );        //system voltage and current
        if(fSysVolt < sys_data.isus_vin_min_volt)      //ISUS_SYS_V_MIN)
        {
            if(mmsDbgFlg==1) { uprintf("DBG: Input Voltage, %.2f v, is too low, aborting BAKE command.  Current = %.3f a", fSysVolt, fSysAmp);  prn_nl(); }

            mms_lampPwrOff();

            sprintf(ebuf, "BAKE command aborted, Sys Vin: %.2f < %.2f, Iin: %.2f mA", fSysVolt, sys_data.isus_vin_min_volt, fSysAmp*1000);
            LogErrorMsg(ebuf, (int)LAMP_PWR_ON_ABORT_VINMIN, apf_eeprom.sys_message_enable);

            return( ERROR );    //-1
        }

        ina260_lamp_voltageCurrent( &fLampVolt, &fLampAmp );        //system voltage and current

        if(bme280_read_data() == 1)
        {
            fBrdTempC = bme280_get_fTemperature();
            fBrdPress = bme280_get_fPressure();
            uiHumidity = bme280_get_uiHumidity();
        }

        //delay_sec(1);
        ticks = (Timer_1ms() - tickStart);
        fTimeLeft = (float)(((bakeTimeSec*1000) - ticks)/1000);
        if(ticks > (bakeTimeSec*1000) )
        {
            abortFlg = 1;
        }


        if(UARTrdy() > 0)
        {
            rxByte = getChar();
            switch(rxByte)
            {

                case 'Q':
                case 'q':
                case 'X':
                case 'x':
                case ESC:
                case CTRL_C:
                    abortFlg = 1;
                    uprintf("ACK,BAKE,%u", ticks/1000); prn_nl();   //print how long it did bake
                    break;

                case 'l':
                case 'L':
                    sprintf(ebuf, "ACK,BAKE,STAT,Vin=%.2f, Iin=%.2f, Vuv=%.2f, Iuv=%.2f, TmpC=%.2f, Pres=%.2f, Hum=%u, Endtime %.3f sec", fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, fBrdPress, uiHumidity, fTimeLeft);
                    LogErrorMsg(ebuf, (int)LAMP_BAKE_DIAG, apf_eeprom.sys_message_enable);
                    uprintf("ACK"); prn_nl(); //let user know the L was good.
                    break;

                case 'w':
                case 'W':
                    uprintf("ACK");  prn_nl();
                    break;

                case '?':
                    uprintf("ACK,BAKE,STAT,Vin=%.2f, Iin=%.2f, Vuv=%.2f, Iuv=%.2f, TmpC=%.2f, Pres=%.2f, Hum=%u, Endtime %.3f sec", fSysVolt, fSysAmp, fLampVolt, fLampAmp, fBrdTempC, fBrdPress, uiHumidity, fTimeLeft);
                    prn_nl();
                    break;

                default:
                    //uprintf("NAK, X to exit BAKE"); prn_nl();
                    break;
            }
        }



        if(abortFlg == 1)
            break;

    }

    mms_lampPwrOff();

    uprintf("ACK,BAKE,DONE"); prn_nl();

    if(abortFlg == 1)
    {
        return( ERROR );
    }
    else
    {
        return 1;
    }

}




void apf_test_dura(uint8_t testNum)
{

    // stream APF dura readings
    while(1)
    {
        apf_take_sample_pH(1);
        if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();

        while(1)
        {
            if( kbhit() )
            {
                return;
            }
        }
    }

}

void apf_test_log(uint8_t testNum)
{
    int avail;
    uint32_t  indx;

    //testNum == 1  error.log

    //APFTODO use this for testing error.log, etc.   incomplete, needs work 16apr2025

    avail = 123;

    for(indx=0; indx<10; indx++)
    {
        avail += 5;
        sprintf((char*)eBuf, "FLASH Card is Full with %ld Bytes Remaining", avail);
        LogErrorMsg((char*)eBuf, (int)FLASH_CARD_FULL, apf_eeprom.sys_message_enable);

    }


}


#ifdef NOCODE
    FR_OK = 0,              /* (0) Function succeeded */
    FR_DISK_ERR,            /* (1) A hard error occurred in the low level disk I/O layer */
    FR_INT_ERR,             /* (2) Assertion failed */
    FR_NOT_READY,           /* (3) The physical drive does not work */
    FR_NO_FILE,             /* (4) Could not find the file */
    FR_NO_PATH,             /* (5) Could not find the path */
    FR_INVALID_NAME,        /* (6) The path name format is invalid */
    FR_DENIED,              /* (7) Access denied due to a prohibited access or directory full */
    FR_EXIST,               /* (8) Access denied due to a prohibited access */
    FR_INVALID_OBJECT,      /* (9) The file/directory object is invalid */
    FR_WRITE_PROTECTED,     /* (10) The physical drive is write protected */
    FR_INVALID_DRIVE,       /* (11) The logical drive number is invalid */
    FR_NOT_ENABLED,         /* (12) The volume has no work area */
    FR_NO_FILESYSTEM,       /* (13) Could not find a valid FAT volume */
    FR_MKFS_ABORTED,        /* (14) The f_mkfs function aborted due to some problem */
    FR_TIMEOUT,             /* (15) Could not take control of the volume within defined period */
    FR_LOCKED,              /* (16) The operation is rejected according to the file sharing policy */
    FR_NOT_ENOUGH_CORE,     /* (17) LFN working buffer could not be allocated or given buffer is insufficient in size */
    FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
    FR_INVALID_PARAMETER    /* (19) Given parameter is invalid */

    FA_READ             Specifies read access to the file. Data can be read from the file.
    FA_WRITE            Specifies write access to the file. Data can be written to the file. Combine with FA_READ for read-write access.
    FA_OPEN_EXISTING    Opens the file. The function fails if the file is not existing. (Default)
    FA_CREATE_ALWAYS    Creates a new file. If the file is existing, the file is truncated and overwritten.
    FA_CREATE_NEW       Creates a new file. The function fails if the file is existing.
    FA_OPEN_ALWAYS      Opens the file. If it is not exist, a new file is created.
    FA_OPEN_APPEND      Same as FA_OPEN_ALWAYS except the read/write pointer is set end of the file.
#endif

// "ftst"
// serial command string is in rx0.buf
void apf_file_test(void)
{
    FIL   coef_fil;

    uprintf("apf_file_test()");   prn_nl();

    fRes = f_open(&coef_fil, "\\ISUS\\ECOEFZER.CAL", FA_READ);   //FR_OK=0, FR_NO_FILE=4
    uprintf("DBG: ISUS\\ECOEFZER.CAL fRes = %d", fRes); prn_nl();
    if(fRes)
    {
        uprintf("DBG: fileopen failed %d", fRes); prn_nl();
        return;
    }
    f_close(&coef_fil);
    return;


}

// "dbg"
void apf_dbg(int state)
{
    if(mmsDbgFlg == 1) { mmsDbgFlg = 0;  uprintf("mmsDbgFlg=0, mms dbg printfs are off"); prn_nl(); }
    else {mmsDbgFlg = 0;  uprintf("mmsDbgFlg=1, mms dbg printfs are on"); prn_nl(); }
}

void apf_serial_term_echo(int state, int prnFlg)
{
    if(state == 0)
    {
        sys_data.echo_flag = 0;
        if(prnFlg) { uprintf("echo off"); prn_nl(); }
    }
    if(state == 1)
    {
        sys_data.echo_flag = 1;
        if(prnFlg) { uprintf("echo on"); prn_nl(); }
    }
    if(state == -1)
    {
        if(sys_data.echo_flag == 0)
        {
            if(prnFlg) { uprintf("echo off"); prn_nl(); }
        }
        else if(sys_data.echo_flag == 1)
        {
            if(prnFlg) { uprintf("echo on"); prn_nl(); }
        }
        else
        {
            if(prnFlg) { uprintf("echo not defd, turning echo on"); prn_nl(); }
            sys_data.echo_flag = 1;
        }

    }
}

// "reade" "readecoef"
void apf_ecoef_read(void)
{
    apf_ecoef_print();
}


void apf_apfArray_debug(void)
{
    uint32_t  indx;
    uint32_t  jndx;

    wait_consoleTx();

    uprintf("SwCalTemp = %.5f", apfArray.SwCalTemp); prn_nl();

    uprintf("ModuleNum = %u", apfArray.ModuleNum); prn_nl();

    uprintf("ZcoefsCnt = %u", apfArray.ZcoefsCnt); prn_nl();

    prn_nl();

    for(indx=0; indx<5; indx++)
    {
        uprintf("C%u' %.5E", indx, apfArray.ZeissCoefs[indx]); prn_nl();

    }
    prn_nl();


    uprintf("apfArray.EcoefsCnt = %u,  apfArray.EcoefFile[][]:", apfArray.EcoefsCnt); prn_nl();
    if( (apfArray.EcoefsCnt > 0) && (apfArray.EcoefsCnt <= 256) )
    {
        for(jndx=0; jndx<3; jndx++)
        {
            prn_nl();  uprintf("  -- apfArray.EcoefFile[0][%u]  --", jndx); prn_nl();
            //delay_msec(200);

            for(indx=0; indx<apfArray.EcoefsCnt; indx++)
            {
                //delay_msec(50);
                wdog_pet();
                uprintf("%.5E", apfArray.EcoefFile[indx][jndx]);
                if((indx+1)%8 == 0 )
                {
                    wait_consoleTx();
                    prn_nl();
                }
                else
                {
                    uprintf(",");
                }
            }

        }

    }
    prn_nl();


    uprintf("apfArray.EcoefsCnt = %u,  apfArray.EcoefData[][]:", apfArray.EcoefsCnt); prn_nl();
    if( (apfArray.EcoefsCnt > 0) && (apfArray.EcoefsCnt <= 256) )
    {
        for(jndx=0; jndx<3; jndx++)
        {
            prn_nl();  uprintf("  -- apfArray.EcoefData[0][%u]  --", jndx); prn_nl();

            for(indx=0; indx<apfArray.EcoefsCnt; indx++)
            {
                wdog_pet();
                if(apf_EcoefData[indx][jndx] <  1.0e-100)
                {
                    apf_EcoefData[indx][jndx] = 0.0;       //uprintf does not handle dnormal  IEE754 not fully supported by the rts lib
                }
                uprintf("%.5E", apf_EcoefData[indx][jndx]);
                if((indx+1)%8 == 0 )
                {
                    wait_consoleTx();
                    prn_nl();
                }
                else
                {
                    uprintf(",");
                    wait_consoleTx();
                }
            }

        }

    }

    prn_nl();

    uprintf("apfArray.LambdaData[]:"); prn_nl();
    if( (apfArray.EcoefsCnt > 0) && (apfArray.EcoefsCnt <= 256) )
    {

        for(indx=0; indx<apfArray.EcoefsCnt; indx++)
        {
            wdog_pet();

            uprintf("%.5E", apfArray.LambdaData[indx]);
            if((indx+1)%8 == 0 )
            {
                wait_consoleTx();
                prn_nl();
            }
            else
            {
                uprintf(",");
            }
        }
    }
    prn_nl();

    uprintf("ZerosCnt = %u,  apfArray.ZeroData[]:", apfArray.ZerosCnt); prn_nl();
    if( (apfArray.ZerosCnt > 0) && (apfArray.ZerosCnt <= 256) )
    {
        for(indx=0; indx<apfArray.ZerosCnt; indx++)
        {

            wdog_pet();
            uprintf("%.5E", apfArray.ZeroData[indx]);
            if((indx+1)%8 == 0 )
            {
                wait_consoleTx();
                prn_nl();
            }
            else
            {
                uprintf(",");
            }
        }
    }
    prn_nl();
    //uprintf("apfArray.TcompCoefs[]:"); prn_nl();
    prn_nl();
    for(indx=0; indx<10; indx++)
    {
        uprintf("TcompCoefs[%u] = %.5E", apfArray.TcompCoefs[indx]); prn_nl();
    }
    prn_nl();

}


//"ecoef"  //
void apf_ecoef_print(void)
{
    uint32_t  indx;

    //load ecoef from fram
    fram_apfArray_refresh();        //fixed bug 24 Jul 2025 v5.1.8

    uprintf("Zcoef Cnt = %u", apfArray.ZcoefsCnt); prn_nl();
    uprintf("Ecoef Cnt = %u", apfArray.EcoefsCnt); prn_nl();
    uprintf("Zeros Cnt = %u", apfArray.ZerosCnt);  prn_nl();
    uprintf("SwCalTemp %.5E", apfArray.SwCalTemp); prn_nl();


    //uprintf("C0' %.5E", apfArray.ZeissCoefs[0]); prn_nl();   //+1.89757E+02   NOTE: does not print leading +,
    //uprintf("C1' %.5E", apfArray.ZeissCoefs[1]); prn_nl();          //+7.81499E-01
    //uprintf("C2' %.5E", apfArray.ZeissCoefs[2]); prn_nl();   //C2' +1.40802E-04
    //uprintf("C3' %.5E", apfArray.ZeissCoefs[3]); prn_nl();   //C3' -3.00259E-07
    //uprintf("C4' %.5E", apfArray.ZeissCoefs[4]); prn_nl();   //+0.0


    prn_nl();
           //1.90080E+02, 0.00000E+00, 0.00000E+00, 0.00000E+00, 9.41317E+03,   0.00000E+00, 0.00000E+00
    uprintf("  Lambda,   EcoefF[n][0], EcoefF[n][1],EcoefF[n][2], ZeroData,     EcoefD[n][1],EcoefD[n][2]"); prn_nl();
    if( (apfArray.EcoefsCnt > 0) && (apfArray.EcoefsCnt <= 256) )
    {

        //for(indx=0; indx < apfArray.EcoefsCnt; indx++)
        for(indx=0; indx < 257; indx++)
        {
            //delay_msec(50);
            wdog_pet();
            uprintf("%03u, ", indx);
            uprintf("%.5E, ", apfArray.LambdaData[indx]);
            //uprintf("%.5E", apfArray.EcoefFile[indx][jndx]); prn_nl();
            uprintf("%.5E, ", apfArray.EcoefFile[indx][0]);  //APFTODO hacking add 1
            uprintf("%.5E, ", apfArray.EcoefFile[indx][1]);
            uprintf("%.5E, ", apfArray.EcoefFile[indx][2]);

            uprintf("%.5E," , apfArray.ZeroData[indx]);
            uprintf("   ");
            uprintf("%.5E, ", apf_EcoefData[indx][0]);
            uprintf("%.5E ", apf_EcoefData[indx][1]);
            prn_nl();
            wait_consoleTx();
        }

    }
    prn_nl();





}


// "readz" "readzcoef"
void apf_zcoef_read(void)
{
    //fram_apf_vars_refresh();
    fram_apfArray_refresh();

    init_zcoef_ecoef_apfArray_into_fram();   // calls LoadIsusZCoefs() apf_util.c, saves array to fram
}

// "zcoef"
// print zcoef in a format that can be copied from serial terminal to file by the user
void apf_zcoef_print(void)
{
    fram_apfArray_refresh();

    uprintf("//* Zeiss MMS-UV, Module Number %u *************************************//", apfArray.ModuleNum); prn_nl();
    uprintf("//* Polynomial Coefficients C#' f(N+1) for pixel to wavelength calculation *//"); prn_nl();
    uprintf("//* DO NOT MODIFY FORMAT, i.e, values MUST be preceded by their coefficient*//"); prn_nl();
//        f_printf(&fil, "//* designator AND comment strings must have either a "//" or "*" in them.  *//"); prn_nl();
//        f_printf(&fil, "//**************************************************************************//"); prn_nl();
    //C0 *= -1.0;

    //uprintf("Zeiss MMS-UV, Module Number %u", apfArray.ModuleNum); prn_nl();
    uprintf("C0' %.5E", apfArray.ZeissCoefs[0]); prn_nl();   //+1.89757E+02   NOTE: does not print leading +,
    uprintf("C1' %.5E", apfArray.ZeissCoefs[1]); prn_nl();          //+7.81499E-01
    uprintf("C2' %.5E", apfArray.ZeissCoefs[2]); prn_nl();   //C2' +1.40802E-04
    uprintf("C3' %.5E", apfArray.ZeissCoefs[3]); prn_nl();   //C3' -3.00259E-07
    uprintf("C4' %.5E", apfArray.ZeissCoefs[4]); prn_nl();   //+0.0
    //uprintf("Zcoef Cnt = %u", apfArray.ZcoefsCnt); prn_nl();
}

void apf_wdog_status(void)
{
    uprintf("DEV: wdog.longest = %u, tot = %u", wdog.tickLongest, wdog.tickTot); prn_nl();  uprintf(">");
}

//"dev"
void apf_print_devStatus(void)
{
    prn_nl(); uprintf("Commands implemented: "); prn_nl();
    prn_nl(); uprintf("  W - ping for ACK"); prn_nl();
    prn_nl(); uprintf("  M - menu"); prn_nl();
    prn_nl(); uprintf("  T2 - needs data checked, CTD values need init and then set by command.  pHext should use apf.ctd values"); prn_nl();
    prn_nl(); uprintf("  BAKE is half baked"); prn_nl();
    prn_nl(); uprintf("  MODE - set get MODE values"); prn_nl();
    prn_nl(); uprintf("  INIT - initialize MSC values"); prn_nl();
    prn_nl(); uprintf("  TINT - set get integration"); prn_nl();
    prn_nl(); uprintf("  C - get Config settings for MSC"); prn_nl();
    prn_nl(); uprintf("  H - Help.  Sets time term for H2M"); prn_nl();
    prn_nl(); uprintf("  SYS - new, Display CF2 power on status"); prn_nl();
    prn_nl(); uprintf("  T1 - nitrate in progress, working on "); prn_nl();
    wait_consoleTx();
    prn_nl(); uprintf("  SL - send last, needs work, store in FRAM?  T1, T2, TS etc"); prn_nl();
    prn_nl(); uprintf("  TS - mode governed: 0 for legacy pH, 1 for nitrate, 2 for pH.  use T1 or T2"); prn_nl();
    prn_nl(); uprintf("  E - get Error counter"); prn_nl();
    prn_nl(); uprintf("  P - get PowerOn counter"); prn_nl();
    prn_nl(); uprintf("  S - get Sample counter"); prn_nl();
    prn_nl(); uprintf("  T - get Time for last sample conversion"); prn_nl();
    prn_nl(); uprintf("  Q - get supported MSC sensors"); prn_nl();
    prn_nl(); uprintf("  SLP - hibernate"); prn_nl();
    prn_nl(); uprintf("  OFF - hibernate sent by APF to prep for power off"); prn_nl();
    prn_nl(); uprintf("  RTC - set get rtc epoch time"); prn_nl();
    prn_nl(); uprintf("  CTD - set get CTD values"); prn_nl();

    wait_consoleTx();

    prn_nl(); uprintf("Commands partial: "); prn_nl();
    prn_nl(); uprintf("  SPECTRA - set get ISUS spectra window values"); prn_nl();
    prn_nl(); uprintf("  FIT - set get isus fit window"); prn_nl();
    prn_nl(); uprintf("  SE - get MSC error log"); prn_nl();
    prn_nl(); uprintf("  CAL - get MSC Ecoefs file"); prn_nl();

    prn_nl(); uprintf("Commands not yet: "); prn_nl();


    prn_nl(); uprintf("Misc: "); prn_nl(); ;

    uint32_t  usd_total, usd_free;

    get_storage_stat(&usd_total, &usd_free);

    prn_nl(); uprintf("DBG serial_cmd_fxn.c MicroSD: %uK bytes total, %uK bytes free", usd_total, usd_free); prn_nl(); prn_nl();

    //prn_nl(); uprintf("Commands not planned: "); prn_nl();
    //prn_nl(); uprintf("  TS is not supported for APF, use T1 or T2"); prn_nl();
    //prn_nl(); uprintf("  MODE is not supported for APF, use T1 or T2, not TS"); prn_nl();



}


void apf_send_last_nitrate(void)
{
    int strSize;

    //uprintf("apf_send_last_nitrate, apf_util.c, Imp in prog %u", apf_fram.sendlast_byteCnt); prn_nl(); //DEBUG

    if(apf_fram.sendlast_byteCnt == 0)
    {
        uprintf("NAK,SL"); prn_nl();
        return;
    }

    //fram_last_samp_retrieve();
    fram_apf_dbuf_retrieve(prnBuf, apf_fram.sendlast_byteCnt);

    //print_sampl();  //format the binary data into prnBuf
    //print_apf_nitrate_sampl();

    //APFTODO, check for null term before printing
    strSize = (int) strlen((char *)prnBuf);

    if( (strSize > 0) && (strSize < SIZEOF_PRNBUF) )
    {
        UARTwrite((char *)prnBuf, strSize);
        prn_nl();
    }
    else
    {
        uprintf("NAK,SL"); prn_nl();
    }

}




void apf_send_last_pH(void)
{
    int strSize;

    //uprintf("apf_send_last_pH, apf_util.c, Imp es Nada"); prn_nl();

    if(apf_fram.sendlast_byteCnt == 0)
    {
        uprintf("DBG: apf_send_last_pH, byteCnt is 0"); prn_nl();
        uprintf("NAK,SL"); prn_nl();
        return;
    }

    fram_apf_dbuf_retrieve(prnBuf, apf_fram.sendlast_byteCnt);       //defd in i2c_fram.c

    //print_apf_pH_sampl();
    strSize = (int) strlen((char *)prnBuf);
    if( (strSize > 0) && (strSize < SIZEOF_PRNBUF) )
    {
        UARTwrite((char *)prnBuf, strSize);
        //prn_nl();
    }
    else
    {
        uprintf("NAK,SL"); prn_nl();
    }


}

// "sys"
void apf_get_sysinfo(void)
{
    DisplaySystemInfo();
}



// 'w' ping response
void apf_print_ping_response(void)
{
    uprintf("ACK");  prn_nl();  // apf spec doesnt mention cr-lf,  CF2 implementation is \n only
}

void apf_bake_uv_lamp(void)
{
    uint32_t bakeSec;
    int     result;
    char *endptr;
    char *str;
    char *strptr;


    //APF: BAKE,NN seconds (1 to 90)
    //MSC: ACK,BAKE,CMD,NN
    //APF: ACK      if MSC does not receive ACK in 3 sec, command is rejected
    //MSC: ACK,BAKE,STAT  or NAK,BAKE,STAT if something is wrong with the lamp
    /*
     * Example: Apf11 sends BAKE,Seconds and MSC immediately sends back ACK,BAKE,CMD,Seconds
     * to acknowledge the command. If both passed values agree, Apf11 sends an ACK. If the
     * MSC doesn’t receive an ACK, within 3 seconds the command is rejected. If the value sent
     * doesn’t pass range checking the MSC will reply with a NAK,BAKE,ErrorMsg. After accepting the command
     * the MSC will acknowledge the state of the lamp cycling by returning
     * ACK,BAKE,STAT if successful or NAK,BAKE,STAT if unsuccessful
     */

    bakeSec = 0;
    str = (char *)&rx0.buf[0];      // skip over bake command
    strtok(str, ",");               // first token is "bake"
    strptr = strtok(NULL, ",");     // second token is bakeSec
    if (strptr == 0)
    {
        uprintf("NAK,BAKE,FormatErr"); prn_nl();
        return;
    }

    errno = 0;
    bakeSec = strtol(strptr, &endptr, 10);
    if (errno == ERANGE)
    {
        uprintf("NAK,BAKE,OverflowErr: %s", strptr); prn_nl();
        return;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,BAKE,ConvertErr: %s", strptr); prn_nl();
        return;
    }


    if( (bakeSec >= 1) && (bakeSec <= 90) )
    {
        // Good Bake
        uprintf("ACK,BAKE,CMD,%u", bakeSec); prn_nl();

        //wait for ACK
        result = waitACK(GET_ACK_TIMEOUT_MSEC);  //3000
        if(result)
        {
            led_sampling_on();

            if( mms_bake_handler(bakeSec) )
            {
                //uprintf("ACK,BAKE,DONE"); prn_nl();       //APFTODO, in future release change the behaviour of bake
            }

            led_sampling_off();
        }
        else
        {
            uprintf("NAK,BAKE,ACK Failure"); prn_nl();
            LogErrorMsg("BAKE Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        }
    }
    else
    {
        uprintf("NAK,BAKE,RangeErr [1,90]: %u", bakeSec); prn_nl();
    }


    //uprintf("DEBUG Bake for 10 seconds");  prn_nl();
    //mms_bake(10);     // secs, defd in mms_spec.c
    //uprintf("BAKE done");  prn_nl();
}





// 'c1'  "c1"
void apf_isus_cfg_get(void)
{
    print_isus_header();
}

// 'c2'  "c2"
void apf_dura_cfg_get(void)
{
    print_dura_header();
}

// 'c' "c"
void apf_cfg_get(void)
{
    if( apf_fram.sys_sensor_mode == ISUS_SENSOR_MODE )
    {
        print_isus_header();
    }
    else if( apf_fram.sys_sensor_mode == DURA_SENSOR_MODE )
    {
        print_dura_header();
    }
    else
    {
        uprintf("NAK,C,InvalidModeValue: %d", apf_fram.sys_sensor_mode);  prn_nl();
    }

}

//"CAL"
void apf_cal_get(void)
{
    int err;

    if( apf_fram.sys_sensor_mode == ISUS_SENSOR_MODE )
    {
        err = read_print_ecoefs_Cal_file();       //apf_util.c
        if (err > 0)
        {
            //uprintf("ACK,CAL,OK"); prn_nl();
        }
        else
        {
            uprintf("NAK,CAL,ACK Failure"); prn_nl();
            LogErrorMsg("CAL Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        }
    }
    else if( apf_fram.sys_sensor_mode == DURA_SENSOR_MODE )
    {
       err = read_print_pH_Cal_file();       //apf_util.c
       if (err > 0)
       {
           //uprintf("ACK,CAL,OK"); prn_nl();
       }
       else
       {
           uprintf("NAK,CAL,ACK Failure"); prn_nl();
           LogErrorMsg("CAL Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
       }
    }
    else
    {
        uprintf("NAK,C,InvalidModeValue: %d", apf_fram.sys_sensor_mode);  prn_nl();
    }

}

//CTD,?
//ACK,CTD?,Time,T,S,P
void apf_ctd_get(void)
{
    uprintf("ACK,CTD?,%lu,%.4f,%.4f,%.2f", apf.CTD_Time, apf.CTD_Temp, apf.CTD_Salinity, apf.CTD_Depth);  prn_nl();
}

#define MIN_CTD_DATE_VALUE  173445218
//#define MAX_CTD_DATE_VALUE  3979434495

void apf_ctd_help(void)
{
    uint32_t  timenow;

    timenow = read_hib_rtcc();
    uprintf("Example CTD set command:  CTD,%u,20.0000,35.0000,-5.00, after response send ACK, ", timenow); prn_nl();
    uprintf("      expected response: ACK,CTD,%u,20.0000,35.0000,-5.00", timenow); prn_nl();
    uprintf("Example CTD get command:  CTD,?"); prn_nl();
    uprintf("ctd,h"); prn_nl();
}




// CTD,timestamp,T,S,P
//DTE 0> W
//DCE 0> ACK
//DTE 0> CTD,1727464446,20.0000,35.0000,-5.00
//DCE 0> ACK,CTD,1727464446,20.0000,35.0000,-5.00
//DTE 0> ACK
void apf_ctd_set(void)
{
    //char str[] = "CTD,1727464446,20.0000,35.0000,-5.00";
    char *str;
    char *endptr;
    char *strptr2, *strptr3, *strptr4, *strptr5;
    int  result;
    //int32_t uiTime;
    uint32_t uiTime;
    float    fTempr;
    float    fSalin;
    float    fDepth;        // AKA Pressure
    int      retStatus = 0;

    //01234567890123456789012345678901234567890
    //CTD,1726526635,20.0000,35.0000,-5.00 (no cr-lf), print exact response with cr-lf

    str = (char *)&rx0.buf[0];

    // Tokenize the string using commas as delimiters
    strtok(str, ",");  // first token CTD command
    strptr2 = strtok(NULL, ",");  // 2nd  token - first argument is Time
    strptr3 = strtok(NULL, ",");  // 3rd token
    strptr4 = strtok(NULL, ",");  // 4th token
    strptr5 = strtok(NULL, ",");  // 5th token
    if ((strptr2 == 0) || (strptr3 == 0) || (strptr4 == 0) || (strptr5 == 0))
    {
        uprintf("NAK,CTD,FormatErr"); prn_nl();
        return;
    }

    if(*strptr2 == '-')
    {
        uprintf("NAK,CTD,ESignErr: %s", strptr2); prn_nl();
        uiTime = 0;
        retStatus = -99;

    }

    errno = 0;
    // Parse the timestamp as a 32-bit unsigned integer
    uiTime = strtoul(strptr2, &endptr, 10);
    //Time Errors:
#ifdef NOCODE
    if(uiTime < 0)
    {
        uprintf("NAK,CTD,ESignErr: %s", strptr2); prn_nl();
        uiTime = 0;
        retStatus = -99;

    }
#endif
    if( uiTime == 0 )
    {
        uprintf("NAK,CTD,EZeroErr: %s", strptr2); prn_nl();
        uiTime = 0;
        retStatus = -1;
    }
#ifdef NOCODE
    else if( uiTime < MIN_CTD_DATE_VALUE )
    {
        uprintf("NAK,CTD,ERangeErr: %s", strptr2); prn_nl();
        uiTime = 0;
        retStatus = -1;
    }
#endif
#ifdef NOCODE
    else if( uiTime > MAX_CTD_DATE_VALUE )
    {
        uprintf("NAK,CTD,ERangeErr: %s", strptr2); prn_nl();
        uiTime = 0;
        retStatus = -1;
    }
#endif
    else if( errno == ERANGE )
    {
        uprintf("NAK,CTD,EOverflowErr: %s", strptr2); prn_nl();
        uiTime = 0;
        retStatus = -1;
    }
    else if( errno != 0 ) // Technically, C99 says only ERANGE may be set, but certain platforms return EINVAL as a non-standard extension
    {
        uprintf("NAK,CTD,EOverflowErr: %s", strptr2); prn_nl();
        uiTime = 0;
        retStatus = -2;
    }
    else if((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,CTD,EConvertErr: %s", strptr2); prn_nl();
        retStatus = -3;
    }
    if (retStatus != 0)
    {
        return; // retStatus;
    }

    errno = 0;
    fTempr = strtof(strptr3, &endptr);
    if( errno == ERANGE )
    {
        uprintf("NAK,CTD,TOverflowErr: %s", strptr3);
        prn_nl();
        retStatus = -4;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,CTD,TConvertErr: %s", strptr3);
        prn_nl();
        retStatus = -5;
    }
    else if( (fTempr < -10.0) || (fTempr > 35.0) )
    {
        uprintf("NAK,CTD,TRangeErr [-10,35.0]: %s", strptr3);
        prn_nl();
        retStatus = -6;
    }
    if (retStatus != 0)
    {
        return; // retStatus;
    }

    errno = 0;
    fSalin = strtof(strptr4, &endptr);
    if( errno == ERANGE )
    {
        uprintf("NAK,CTD,SOverflowErr: %s", strptr4);
        prn_nl();
        retStatus = -7;
    }
    else if((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,CTD,SConvertErr: %s", strptr4);
        prn_nl();
        retStatus = -8;
    }
    else if( (fSalin < -0.25) || (fSalin > 40.0) )
    {
        uprintf("NAK,CTD,SRangeErr [-0.25,40.0]: %s", strptr4);
        prn_nl();
        retStatus = -9;
    }
    if (retStatus != 0)
    {
        return; // retStatus;
    }

    errno = 0;
    fDepth = strtof(strptr5, &endptr);
    if( errno == ERANGE )
    {
        uprintf("NAK,CTD,POverflowErr: %s", strptr5);
        prn_nl();
        retStatus = -10;
    }
    else if((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,CTD,PConvertErr: %s", strptr5);
        prn_nl();
        retStatus = -11;
    }
    else if( (fDepth < -200.0) || (fDepth > 5000.0) )
    {
        uprintf("NAK,CTD,PRangeErr [-200.0,5000.0]: %s", strptr5);
        prn_nl();
        retStatus = -12;
    }
    if (retStatus != 0)
    {
        return; // retStatus;
    }

    apf.CTD_Time = uiTime;
    apf.CTD_Temp = fTempr;
    apf.CTD_Salinity = fSalin;
    apf.CTD_Depth = fDepth;

    uprintf("ACK,CTD,%lu,%.4f,%.4f,%.2f", apf.CTD_Time, apf.CTD_Temp, apf.CTD_Salinity, apf.CTD_Depth); prn_nl();

    result = waitACK(GET_ACK_TIMEOUT_MSEC);
    if( ! result )     // if no ACK, vals get dumped
    {
        //uprintf("killing CTD values"); prn_nl();		//DBG APFTODO, need to do this in timeout handler
        apf.CTD_Time     = 0;   //no ACK no keepy vals
        apf.CTD_Temp     = -1.0;
        apf.CTD_Salinity = -1.0;
        apf.CTD_Depth    = -1.0;

        uprintf("NAK,CTD,ACK Failure"); prn_nl();
        LogErrorMsg("CTD Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
    }
    else
    {
        if( fram_apf_vars_store() <= 0)
        {
            sprintf((char*)prnBuf, "CTD Command FRAM Failure");
            LogErrorMsg((char*)prnBuf, (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
        }
    }
}

//wait for ACK, timeout in msec
// return TRUE if ACK received, FALSE if not ACK, -1 if timedout
int waitACK(uint32_t timeout_ms)
{
    unsigned char uc;
#define SIZEOF_WAITACK_RXBUF  16
    unsigned char rxBuf[SIZEOF_WAITACK_RXBUF];
    uint32_t ticks;
    uint32_t indx;

    uint32_t tickStart = Timer_1ms();

    indx=0;
    while(1)
    {
        if( UARTRxBytesAvail() )
        {
            uc = getChar();

            switch(uc)
            {
            case 'A':
            case 'a':
            case 'C':
            case 'c':
            case 'K':
            case 'k':
                //uprintf("DBG: good char %c", uc); prn_nl();
                rxBuf[indx] = uc;
                indx++;
                break;

            default:
                return(false);
            }

#define SIZEOF_ACK_STRING 3
            if(indx >= SIZEOF_ACK_STRING)
            {
                //compare to ACK
                if ( (strncmp((const char*)rxBuf, "ACK", 3) == 0) || (strncmp((const char*)rxBuf, "ack", 3) == 0) )
                {
                    //handle the 3 cases 1)no termination, 2) cr terminated, 3) cr-lf terminated
                    return (true);
                }

                //if strncmp fails, return false
                return(false);
            }
            tickStart = Timer_1ms();
        }

        ticks = (Timer_1ms() - tickStart);
        if(ticks > timeout_ms)
        {
            //Timed out
            return (false);
        }
    }
}

void apf_err_get(void)
{
    unsigned long currentTime = 0;
    // JLD error previously was a weird time shown if the timers were cleared.
    // Maybe we set the timers to the current compile time instead of zero?

    if( apf_fram.sys_sensor_mode == ISUS_SENSOR_MODE )
    {
        if (apf_fram.isus_error_time > TI_CCS_UNIX_EPOCH_OFFSET)
            currentTime = apf_fram.isus_error_time - TI_CCS_UNIX_EPOCH_OFFSET;

        uprintf("ACK,E,%lu,%lu", apf_fram.isus_error_ctr, currentTime);   prn_nl();
    }
    else if( apf_fram.sys_sensor_mode == DURA_SENSOR_MODE )
    {
        if (apf_fram.dura_error_time > TI_CCS_UNIX_EPOCH_OFFSET)
            currentTime = apf_fram.dura_error_time - TI_CCS_UNIX_EPOCH_OFFSET;

        uprintf("ACK,E,%lu,%lu", apf_fram.dura_error_ctr, currentTime);   prn_nl();
    }
    else
    {
        //system errors
        if (apf_fram.sys_error_time > TI_CCS_UNIX_EPOCH_OFFSET)
            currentTime = apf_fram.sys_error_time - TI_CCS_UNIX_EPOCH_OFFSET;

        uprintf("ACK,E,%lu,%lu", apf_fram.sys_error_ctr, currentTime);   prn_nl();
    }

}

// test vector
// fit,217.87,228.76
// NAK,FIT,SConvertErr: 217.87
// 216.10,227.20   is off by more than 1.4
void apf_fit_set(void)
{
    int     intVal;
    char    ebuf[ACKBUFSIZE];

    char *str;
    char *endptr;
    int  result;

    //01234567890123456789012345678901234567890
    //FIT,217,250
    //ACK,FIT,216.9,250.3
    //FIT,217,240
    //ACK,FIT,216.9,239.9
    //FIT,215,245
    //ACK,FIT,215.3,244.7
    intVal = 0;
    str = (char *)&rx0.buf[0];

    //fram_apf_vars_refresh();
    //fram_apfArray_refresh();      // if the array is updated, do nothing, otherwise update from fram



    // Tokenize the string using commas as delimiters
    strtok(str, ",");  // first token guaranteed since it got us here
    char *strptr2 = strtok(NULL, ",");  // 2nd  token
    char *strptr3 = strtok(NULL, ",");  // 3nd  token
    if ((strptr2 == NULL) || (strptr3 == NULL))
    {
        uprintf("NAK,FIT,FormatErr"); prn_nl();
        if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
        return;
    }
    errno = 0; // Reset errno before calling strtoul
	apf_eeprom.lambda_beg = strtoul(strptr2, &endptr, 10);  // string to unsigned long int
	//fVal = strtof(strptr2, &endptr);  // string to float
    if( errno == ERANGE )
    {
        uprintf("NAK,FIT,SOverflowErr: %s", strptr2);  prn_nl();
        intVal = ERROR;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,FIT,SConvertErr: %s", strptr2);   prn_nl();
        intVal = ERROR;
    }
    else if( (apf_eeprom.lambda_beg < 215) || (apf_eeprom.lambda_beg > 300) )
    {
        uprintf("NAK,FIT,SRangeErr [215,300]: %s", strptr2);   prn_nl();
        intVal = ERROR;
    }

    // if there is already an error printed bail now
    if (intVal == ERROR)
    {
        if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
        return;
    }

	errno = 0; // Reset errno before calling strtoul
	apf_eeprom.lambda_end = (uint32_t)strtoul(strptr3, &endptr, 10);
	//fVal = strtof(strptr3, &endptr);

    if( errno == ERANGE )
    {
        uprintf("NAK,FIT,EOverflowErr: %s", strptr3);
        intVal = ERROR;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,FIT,EConvertErr: %s", strptr3); prn_nl();
        intVal = ERROR;
    }
    else if( (apf_eeprom.lambda_end < 215) || (apf_eeprom.lambda_end > 300) )
    {
        uprintf("NAK,FIT,ERangeErr [215,300]: %s", strptr3);  prn_nl();
        intVal = ERROR;
    }

    // If there were no errors check the begin and end for range
    if(intVal == OK)
    {

        if( apf_eeprom.lambda_end <= apf_eeprom.lambda_beg )
        {
            uprintf("NAK,FIT,EVal<SVal: %u, %u", apf_eeprom.lambda_beg, apf_eeprom.lambda_end); prn_nl();
            if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
            intVal = ERROR;
        }

        if( (apf_eeprom.lambda_end - apf_eeprom.lambda_beg) <= 10 )
        {
            uprintf("NAK,FIT,EVal-SVal!>10: %u, %u", apf_eeprom.lambda_beg, apf_eeprom.lambda_end); prn_nl();
            if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
            intVal = ERROR;
        }

        if (intVal == OK)
        {
            uprintf("ACK,FIT,%u,%u", apf_eeprom.lambda_beg, apf_eeprom.lambda_end); prn_nl();
            if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();

            result = waitACK(GET_ACK_TIMEOUT_MSEC);
            if(result)
            {
                fram_apfArray_refresh();      // load the ecoefs apfArray

                if( apfArray.ZcoefsCnt <= 0 )
                {
                    uprintf("NAK,FIT,ZCoef File Error = %u", apfArray.ZcoefsCnt); prn_nl();
                    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                    //SCIRxFlush();
                    return;
                }

                MatchPixelsToLambda(apfArray.LambdaData, apf_eeprom.lambda_beg, apf_eeprom.lambda_end, &apf_eeprom.isus_pixel_beg, &apf_eeprom.isus_pixel_end);

                apf_eeprom.isus_fit_pixels = (apf_eeprom.isus_pixel_end - apf_eeprom.isus_pixel_beg) + 1;

                eeprom_apf_cfg_store();
            }
            else
            {
                uprintf("NAK,FIT,ACK Failure"); prn_nl();
                LogErrorMsg("FIT Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
                if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
            }
        }
        return;
    }

    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();

    return;

}

void apf_fit_get(void)
{
    //fram_apf_vars_refresh();
    fram_apfArray_refresh();      // if the array is updated, do nothing, otherwise update from fram

    if( apfArray.ZcoefsCnt <= 0 )
    {
        uprintf("NAK,FIT,ZCoef File Error = %d", apfArray.ZcoefsCnt); prn_nl();
        //SCIRxFlush();
        return;
    }

    // uprintf("ACK,FIT?,%.1lf,%.1lf", CellNumToLambda(apf_eeprom.isus_pixel_beg), CellNumToLambda(apf_eeprom.isus_pixel_end) ); prn_nl();
    uprintf("ACK,FIT?,%.1lf,%.1lf", apfArray.LambdaData[apf_eeprom.isus_pixel_beg], apfArray.LambdaData[apf_eeprom.isus_pixel_end] ); prn_nl();
}

void apf_profileCount_help(void)
{
    uprintf("profile      - get profile counter"); prn_nl();
    uprintf("profile,?    - get profile counter"); prn_nl();
    uprintf("profile,NNNN - set profile counter [0000 to 9999], requires ACK"); prn_nl();
    uprintf("profile,inc  - increment profile counter by 1, requires ACK"); prn_nl();
    uprintf("profile,dec  - decrement profile counter by 1, requires ACK"); prn_nl();
}

void apf_profileCount_get(void)
{
    uprintf("ACK,PROFILE?,%04u", apf_fram.profileCnt); prn_nl();
}

void apf_profileCount_set(void)
{
    int32_t        profileCnt;
    unsigned char   *strptr;
    char            *endptr;
    int             result;


    //profile,NNNN
    strptr = &rx0.buf[8];
	errno = 0; // Reset errno before calling strtoul
	if (*strptr)
	{
	    profileCnt = strtol((char*)strptr, &endptr, 10);
	}
	else
	{
	    uprintf("NAK,PROFILE,FormatErr"); prn_nl();
	    return;
	}

    if (errno == ERANGE)
    {
        uprintf("NAK,PROFILE,OverflowErr: %s", strptr); prn_nl();
        return;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,PROFILE,ConvertErr: %s"); prn_nl();
        return;
    }

    if(profileCnt >= 0 && profileCnt <= 9999)
    {
        uprintf("ACK,PROFILE,%04u", profileCnt); prn_nl();

        result = waitACK(GET_ACK_TIMEOUT_MSEC);
        if(result)
        {
            apf_fram.profileCnt = profileCnt;

            apf_create_logFile_names();

            fram_apf_vars_store();
        }
        else
        {
            uprintf("NAK,PROFILE,ACK Failure"); prn_nl();
            LogErrorMsg("PROFILE Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        }
        //UARTFlushRx();   //Flush is blocking the following command from being received, APFTODO, look at removing many / all UARTFlushRX
    }
    else
    {
        uprintf("NAK,PROFILE,RangeErr [0,9999]: %04d", profileCnt); prn_nl();
    }

}

// APF11 sends PROFILE,INC to increment the profile count
void apf_profileCount_inc(void)
{
    uint32_t  profileCnt;
    int       result;

    profileCnt = apf_fram.profileCnt;

    if(apf_fram.profileCnt < 9999)
    {
        profileCnt++;
    }
    else
    {
        uprintf("NAK,PROFILE,RangeErr [0,9999]: %04u", apf_fram.profileCnt); prn_nl();
        return;
    }

    //APFTODO error check profileCnt
    //if(profileCnt >=0 && profileCnt <= 9999)
    if (profileCnt <= 9999)
    {
        uprintf("ACK,PROFILE,%04u", profileCnt); prn_nl();

        result = waitACK(GET_ACK_TIMEOUT_MSEC);
        if(result)
        {
            apf_fram.profileCnt = profileCnt;

            apf_fram.profileStartTime = read_hib_rtcc();
            apf_create_logFile_names();

            //write apf data
            fram_apf_vars_store();

        }
        else
        {
            uprintf("NAK,PROFILE,ACK Failure"); prn_nl();
            LogErrorMsg("PROFILE Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        }

    }

    //UARTFlushRx();   //SCIRxFlush();
}


// APF11 sends PROFILE,INC to increment the profile count
void apf_profileCount_inc_test(void)
{
    apf_fram.profileCnt++;

    apf_fram.profileStartTime = read_hib_rtcc();
    apf_create_logFile_names();

    //write apf data
    fram_apf_vars_store();

}

// APF11 sends PROFILE,DEC to decrement the profile count by 1
void apf_profileCount_dec(void)
{
    uint32_t  profileCnt;
    int       result;

    profileCnt = apf_fram.profileCnt;

    if(apf_fram.profileCnt > 0)
    {
        profileCnt--;
    }
    else
    {
        uprintf("NAK,PROFILE,RangeErr [0,9999]: %04u", apf_fram.profileCnt); prn_nl();
        return;
    }

    //APFTODO error check profileCnt
    if(profileCnt <= 9999)
    {
        uprintf("ACK,PROFILE,%04u", profileCnt); prn_nl();

        result = waitACK(GET_ACK_TIMEOUT_MSEC);
        if(result)
        {
            apf_fram.profileCnt = profileCnt;

            apf_fram.profileStartTime = read_hib_rtcc();
            apf_create_logFile_names();

            //write apf data
            fram_apf_vars_store();
        }
        else
        {
            uprintf("NAK,PROFILE,ACK Failure"); prn_nl();
            LogErrorMsg("PROFILE Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        }
    }

    //UARTFlushRx();   //SCIRxFlush();

}

//INIT  init apf persistent vars formerly stored in DS3234
void apf_init_persistent_vars(void)
{
    apf_deployment_init();      // defd in apf_init.c

    uprintf("ACK,INIT"); prn_nl();

    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();   //SCIRxFlush();
}

// print all metadata commands, used for dumping settings before running defaults
// commmand "settings"
void cmd_print_settings(void)
{
    int indx;

    //todo - read fram and eeprom
    fram_apfArray_refresh();

    print_cmd_version();
    apf_baud_get();
    prn_nl();

    cmd_period_get();
    //apf_vinmin_get();
    uprintf("vinmin,isus=%.1f", sys_data.isus_vin_min_volt); prn_nl();
    uprintf("vinmin,dura=%.1f", sys_data.dura_vin_min_volt); prn_nl();
    prn_nl();

    uprintf("cal_date,isus=%s", apfArray.CalDate); prn_nl();
    uprintf("cal_date,k0=%s", sys_data.k0_Cal_date); prn_nl();
    uprintf("cal_date,pt=%s", sys_data.PT_Cal_date); prn_nl();

    for(indx=0; indx<MAX_NUMOF_FP_CAL_FIELDS; indx++)
    {
        uprintf("fp[%u]=%.5e", indx, sys_data.fP_Cal[indx]); prn_nl();
    }
    for(indx=0; indx<MAX_NUMOF_K2_CAL_FIELDS; indx++)
    {
        uprintf("k2[%u]=%.5e", indx, sys_data.K2_Cal[indx]); prn_nl();
    }


    uprintf("isfet_SN=%s", sys_data.isfet_SN); prn_nl();
    uprintf("ise_SN=%s", sys_data.ise_SN); prn_nl();
    uprintf("k0_hcl=%f", sys_data.k0_HCL); prn_nl();
    uprintf("k0_sw=%f", sys_data.k0_SW); prn_nl();
    uprintf("k0std_sw=%f", sys_data.k0std_SW); prn_nl();



    uprintf("PROFILE,%04u", apf_fram.profileCnt); prn_nl();
    uprintf("pH_Calfile=%s", sys_data.cal_filename); prn_nl();
    //apf_sensor_type_get();

    apf_sernum_msc_get();
    apf_sernum_baseboard_get();
    apf_sernum_ctd_get();
    apf_sernum_isfet_get();
    apf_sernum_ise_get();
    apf_sernum_lamp_get();
    apf_sernum_mcap_get();
    apf_sernum_optode_get();
    apf_sernum_probe_get();
    apf_sernum_sensor_get();
    apf_sernum_spec_get();
    apf_sernum_spec_analog_get();
    apf_sernum_spec_digital_get();
    apf_sernum_tiva_get();

    apf_spectra_get();
    uprintf("SPECTRA,%.1lf,%.1lf", apfArray.LambdaData[apf_eeprom.isus_datpixel_beg], apfArray.LambdaData[apf_eeprom.isus_datpixel_end]);  prn_nl();
    uprintf("ACK"); prn_nl();

    //apf_int_time_get();
    uprintf("TINT,%u", apf_eeprom.isus_spec_period); prn_nl();
    uprintf("ACK"); prn_nl();

    //apf_fit_get();
    uprintf("FIT,%.1lf,%.1lf", apfArray.LambdaData[apf_eeprom.isus_pixel_beg], apfArray.LambdaData[apf_eeprom.isus_pixel_end] ); prn_nl();
    uprintf("ACK"); prn_nl();

    //apf_cal_get();  //TODO, expand this without ACK
    //cmd_get_filename();

    //apf_zcoef_print();


    //apf_ecoef_print();

    //get_pH_sensor_cal_coeff();
    //export_pH_sensor_cal_coeff();

    //apf_print_isus_header();
    //apf_print_dura_header();
    //get_header();
    //print_metadata_header( read_hib_rtcc() );

    //cmd_config_get();
    //apf_defaults();   //expand this


    //apf_serial_term_echo(-1, 1);  //maybe

}


//MODE,1   ACK
void apf_mode_set(void)
{
    int32_t mode;
    char *strptr = (char *)&rx0.buf[0];
    char *endptr;

    strtok(strptr, ",");            // first token cmd "MODE"
    strptr = strtok(NULL, ",");     // 2nd  token argument

    //MODE,x
    // x: 0=No APF Sensor (ts gives Carbon lab pH format), 1=ISUS, 2=Dura, 3+ = invalid
    //01234567890
    //MODE,1
    if (strptr == NULL)
    {
        uprintf("NAK,MODE,FormatErr"); prn_nl();
        return;
    }

    errno = 0; // Reset errno before calling strtoul
    mode = strtol((char*)strptr, &endptr, 10);
    if (errno == ERANGE)
    {
        uprintf("NAK,MODE,OverflowErr: %s", strptr); prn_nl();
        return;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,MODE,ConvertErr: %s", strptr); prn_nl();
        return;
    }
    else
    {
        if( ((databarFlg == 0) && (mode >= 0) && (mode <= 2) ) ||
            ((databarFlg == 1) && (mode >= 1) && (mode <= 3) ))
        {
            // Good MODE
            uprintf("ACK,MODE,%d", mode); prn_nl();
        }
        else
        {
            if (databarFlg == 0)
            {
                uprintf("NAK,MODE,RangeErr [0,2]: %d", mode); prn_nl();
            }
            else
            {
                uprintf("NAK,MODE,RangeErr [1,3]: %d", mode); prn_nl();
            }
            return;
        }
    }

    //wait for ACK
    int result = waitACK(GET_ACK_TIMEOUT_MSEC);
    if(result)
    {
        // Only update mode on mode change
        if(apf_fram.sys_sensor_mode != mode)
        {
            apf_fram.sys_sensor_mode  = mode;
            apf_fram.sendlast_byteCnt = 0; // Clear SL command buffer on mode change
            fram_apf_vars_store();
        }

    }
    else
    {
        uprintf("NAK,MODE,ACK Failure"); prn_nl();
        LogErrorMsg("MODE Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
    }

    return;
}

// MODE,? Query the MSC MODE
void apf_mode_get(void)
{
    uprintf("ACK,MODE?,%u", apf_fram.sys_sensor_mode); prn_nl();

}

void apf_off(void)
{
    //APFTODO, look at microSD / NAND / FatFS for any pending writes.
    uprintf("ACK,OFF");  prn_nl();
}

void apf_pc_get(void)
{
    // sys_por_ctr is being renamed to sys_wdog_ctr as wdog_ctr will indicate crashes
    uprintf("ACK,P,%lu,%lu,%lu", apf_fram.sys_wdog_ctr, apf_fram.sys_reset_ctr, apf_fram.sys_reset_time - TI_CCS_UNIX_EPOCH_OFFSET); prn_nl();
}


void apf_sensor_type_help(void)
{
    uprintf("qconfig=N   N is sensor config returned by Q command:  1 = No Sensor, 2 = ISUS Nitrate, 3 = ISFET pH, 4 = Nitrate + pH");  prn_nl();
}

void apf_sensor_type_set(uint32_t sensorCfg)
{
    //handle qconfig or qconfig,?
    if( (sensorCfg == 0) || (sensorCfg == 15) ) //'?'
    {
        uprintf("%u", apf_eeprom.sys_sensor_type); prn_nl();
        return;
    }

    if( (sensorCfg < 1) || (sensorCfg > 4) )
    {
        uprintf("Invalid sensor config %u.  1 = No Sensor, 2 = ISUS Nitrate, 3 = ISFET pH, 4 = Nitrate + pH", sensorCfg);  prn_nl();
        return;
    }

    apf_eeprom.sys_sensor_type = sensorCfg;
    if(eeprom_apf_cfg_store() != 0)
    {
        uprintf("Unable to program eeprom in apf_sensor_type_set"); prn_nl();
        LogErrorMsg("Unable to program eeprom in apf_sensor_type_set", (int)EEPROM_FAIL, apf_eeprom.sys_message_enable);
        return;
    }

    uprintf("Q command MSC Sensor Config: ");
    switch(apf_eeprom.sys_sensor_type)
    {
        case 1:
            uprintf("1 = No Sensor"); prn_nl();
            break;
        case 2:
            uprintf("2 = ISUS Nitrate"); prn_nl();
            break;
        case 3:
            uprintf("3 = ISFET pH"); prn_nl();
            break;
        case 4:
            uprintf("4 = ISUS Nitrate + ISFET pH"); prn_nl();
            break;
        default:
            break;
    }

}


//"Q command
void apf_sensor_type_get(void)
{
    uprintf("ACK,Q,%u", (unsigned int)apf_eeprom.sys_sensor_type); prn_nl();
}




#define JAN2025     1735693200U
#define APR2025     1744688130U
#define JAN2035     2073024030U
#define JAN2038     2145951330U
#define MAX_UINT32  4294967295U
#define MAX_INT32   2145900000U     //31 Dec 2037
#define MAX_TIME_DATE_VALUE  MAX_INT32     //3979434495U          //10 years from uint32_t epoch time rollover in the year 2096


//The 32-bit epoch time rollover, or Year 2038 Problem (Y2K38), happens when a system using a signed 32-bit integer to count Unix time
//(seconds since Jan 1, 1970) hits its maximum value, 2,147,483,647, at 03:14:07 UTC on January 19, 2038, causing the time to "overflow"
//and wrap around to a negative number, effectively rolling back the date to December 13, 1901, breaking time-sensitive applications.
//The fix involves converting systems to use 64-bit integers for time storage, ensuring software can handle larger values beyond the 2038 deadline

void apf_rtc_help(void)
{
    uint32_t    timenow;

    timenow = read_hib_rtcc();

    uprintf("RTC help:");  prn_nl();
    uprintf("   Set RTC:  RTC,%u", timenow); prn_nl();   //04/14/25 8:35:30
    //uprintf("   Set RTC:  RTC,%u",JAN2025); prn_nl();
    //uprintf("   Set RTC:  RTC,%u",APR2025); prn_nl();
    //uprintf("   Set RTC:  RTC,%u",JAN2035); prn_nl();
    //uprintf("   Set RTC:  RTC,%u",JAN2038); prn_nl();
    //uprintf("   Set RTC:  RTC,%u",MAX_UINT32); prn_nl();
    //uprintf("   Set RTC:  RTC,%u",MAX_INT32); prn_nl();
    uprintf("   RTC range[0,%u]", MAX_TIME_DATE_VALUE);
    uprintf("     Warning: RTC is 32bit and has Year 2038 rollover to 1901");  prn_nl();


    uprintf("  Read RTC:  RTC,?"); prn_nl();
    uprintf("See also: time,h"); prn_nl();

    prn_nl(); uprintf("Time now: "); printCurrentTime(); prn_nl();

}

// RTC,1727464426
void apf_rtc_set(void)
{
    //int32_t rtcValue;

    char *strptr = (char *)&rx0.buf[0];
    char *endptr;

    strtok(strptr, ",");            // first token cmd "RTC"
    strptr = strtok(NULL, ",");     // 2nd  token argument

    //RTC,1727464426
    //01234567890123
    if (strptr == NULL)
    {
        uprintf("NAK,RTC,FormatErr"); prn_nl();
        return;
    }

    if(*strptr == '-')
    {
        uprintf("NAK,RTC,SignErr: %s", strptr);  prn_nl();
        return;
    }

    errno = 0; // Reset errno before calling strtoul
    //rtcValue = strtol(strptr, &endptr, 10);
    const uint32_t timeSet = strtoul(strptr, &endptr, 10);
    if (errno == ERANGE)
    {
        uprintf("NAK,RTC,OverflowErr: %s", strptr); prn_nl();
        return;
    }
    else if (errno || *endptr)
    {
        uprintf("NAK,RTC,ConvertErr: %s", strptr); prn_nl();
        return;
    }
#ifdef NOCODE
    else if (rtcValue < 0)
    {
        uprintf("NAK,RTC,SignErr: %d", rtcValue);  prn_nl();
        return;
    }
#endif

    if(timeSet > MAX_TIME_DATE_VALUE)
    {
        uprintf("NAK,RTC,MaxTimeSetErr: %u", timeSet); prn_nl();
        return;
    }

	//const uint32_t timeSet = strtoul(strptr, &endptr, 10);
    //else if ((errno != 0) || (timeSet == 0))
    if (errno != 0)
    {
        //TODO: error handling
        uprintf("NAK,RTC,ConvertErr: %s", strptr); prn_nl();
        return;
    }
    else
    {
        // CCS/TI uses time epoch of Jan 1, 1900
        const uint32_t timeSet_ti_epoch = timeSet + TI_CCS_UNIX_EPOCH_OFFSET;
        uprintf("ACK,RTC,%u,%u", timeSet, timeSet); prn_nl();

        int result = waitACK(GET_ACK_TIMEOUT_MSEC);
        if(result)
        {
            // Set time if ACK'd
            // This means the time setting is delayed by the ACK response time
            MAP_HibernateRTCSet(timeSet_ti_epoch);
        }
        else
        {
            uprintf("NAK,RTC,ACK Failure"); prn_nl();
            LogErrorMsg("RTC Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        }
    }

}

//RTC,?
void apf_rtc_get(void)
{
    const uint32_t timestamp = read_hib_rtcc() - TI_CCS_UNIX_EPOCH_OFFSET;

    uprintf("ACK,RTC?,%lu", timestamp ); prn_nl();
}

//SE
void apf_se_xfer(void)
{
    int err;

    // SendRecords returns 0 on ok, 1 on ok and EOF, or 2 on just EOF
    err = SendRecords(ERRORLOG_FILE, &apf_fram.Efpos);
    switch (err)
    {
        case 0:     // response is in SendRecords ending with EOF
        case 1:     // we hit the EOF
                    // both cases the fpos moved so store
            fram_apf_vars_store();              // save the Efpos
            break;
        case 2:
            break;
        default:
            if ( !microSD_flg)      //if the microSD card is not enabled, report error as FILESYS_FAIL
            {
                uprintf("NAK,SE,FILESYS_FAIL", err); prn_nl();
            }
            else
            {
                uprintf("NAK,SE,%d", err); prn_nl();
            }
    }

    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();   //SCIRxFlush();

    fram_apf_vars_store();
}

void apf_se_get(void)
{
    uprintf("ACK,SE?,%ld", apf_fram.errorLog_errorCnt); prn_nl();  // sys_error_ctr);
}

// get sample counter ("S" command), TGM was hoping to rename the command to SC, but oh well, MSC1 compat rules the roost)
void apf_sc_get(void)
{

    if( apf_fram.sys_sensor_mode == ISUS_SENSOR_MODE )
    {
        uprintf("ACK,S,%lu,%lu", apf_fram.isus_sample_ctr, apf_fram.isus_sample_time - TI_CCS_UNIX_EPOCH_OFFSET);  prn_nl();
    }
    else if( apf_fram.sys_sensor_mode == DURA_SENSOR_MODE )
    {
        uprintf("ACK,S,%lu,%lu", apf_fram.dura_sample_ctr, apf_fram.dura_sample_time - TI_CCS_UNIX_EPOCH_OFFSET);  prn_nl();
    }
    else
    {
        uprintf("NAK,S,InvalidModeValue: %d", apf_fram.sys_sensor_mode);  prn_nl();
    }

}

void apf_slp_sleep(void)
{
    // sleep is handled in serial.c, this function is not needed
    uprintf("ACK,SLP"); prn_nl();
}

void apf_spectra_help(void)
{
    uprintf("Get,Set spectra range for sending over satcomm"); prn_nl();
    uprintf("Get: spectra"); prn_nl();
    uprintf("     spectra,?"); prn_nl();
    uprintf("Set: spectra,217,250"); prn_nl();
    uprintf("Range:      [210,350]"); prn_nl();
    prn_nl();
}

/* SPECTRA,217,250
 * Set reported (see SL command) ISUS spectra data range. This command will be used
 * to implement post-deployment reconfigurability using the float’s remote control mechanism.
 * This typically needs to occur only once, upon initial MSC power up (start of profile). The
 * default (see INIT command) reported spectra data range is [217nm,250nm].
 * Example: Apf11 sends SPECTRA,WindowBegin,WindowEnd and MSC sends back as an
 * acknowledgement, ACK,SPECTRA,WindowBegin,WindowEnd. If both agree, Apf11
 * sends an ACK. If MSC doesn’t receive an ACK, within 3 seconds, the values sent are discarded.
 * If the values sent don’t pass range checking the MSC will reply with a NAK,SPECTRA,ErrorMsg
 *
 * called from serial.c spectra, Expects serial data in rx0.buf[0]
*/
void apf_spectra_set(void)
{
    int     indx;
    char    ebuf[ACKBUFSIZE];

    char *str;
    char *endptr;
    int  result;



    //01234567890123456789012345678901234567890
    //SPECTRA,217,250
    indx = 0;
    str = (char *)&rx0.buf[0];

    // Tokenize the string using commas as delimiters
    strtok(str, ",");  // first token

    char *strptr2 = strtok(NULL, ",");  // 2nd  token
    char *strptr3 = strtok(NULL, ",");  // 3rd  token
    if ((strptr2 == NULL) || (strptr3 == NULL))
    {
        uprintf("NAK,SPECTRA,FormatErr"); prn_nl();
        return;
    }
    errno = 0;
    apf_eeprom.lambda_dat_beg = (uint32_t)strtol(strptr2, &endptr, 10);  // string to signed long int
    if( errno == ERANGE )
    {
        uprintf("NAK,SPECTRA,SOverflowErr: %s", strptr2); prn_nl();
        indx = ERROR;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,SPECTRA,SConvertErr: %s", strptr2);  prn_nl();
        indx = ERROR;
    }
    else if( (apf_eeprom.lambda_dat_beg < 210) || (apf_eeprom.lambda_dat_beg > 350) )
    {
        uprintf("NAK,SPECTRA,SRangeErr [210,350]: %s", strptr2); prn_nl();
        indx = ERROR;
    }
    else if( (apf_eeprom.lambda_dat_beg == 0) && (endptr == strptr2) )
    {
        uprintf("NAK,SPECTRA,SConvertErr: %s", strptr2);  prn_nl();
        indx = ERROR;
    }
    // if there is already an error printed bail now
    if (indx == ERROR)
    {
        if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
        return;
    }
    apf_eeprom.lambda_dat_end = (uint32_t)strtol(strptr3, &endptr, 10);
    if( errno == ERANGE )
    {
        uprintf("NAK,SPECTRA,EOverflowErr: %s", strptr3); prn_nl();
        indx = ERROR;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,SPECTRA,EConvertErr: %s", strptr3);  prn_nl();
        indx = ERROR;
    }
    else if( (apf_eeprom.lambda_dat_end == 0) && (endptr == strptr3) )
    {
        uprintf("NAK,SPECTRA,EConvertErr: %s", strptr3); prn_nl();
        indx = ERROR;
    }
    else if (errno == EINVAL)
    {
        uprintf("NAK,SPECTRA,EConvertErr: %s", strptr2);  prn_nl();
        indx = ERROR;
    }
    else if( (apf_eeprom.lambda_dat_end < 210) || (apf_eeprom.lambda_dat_end > 350) )
    {
        uprintf("NAK,SPECTRA,ERangeErr [210,350]: %s", strptr3); prn_nl();
        indx = ERROR;
    }


    if (indx == ERROR)
    {
        if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
        return;
    }

    if(indx == 0)
    {
        if( apf_eeprom.lambda_dat_end <= apf_eeprom.lambda_dat_beg )
        {
            uprintf("NAK,SPECTRA,EVal<SVal: %d, %d", apf_eeprom.lambda_dat_beg, apf_eeprom.lambda_dat_end); prn_nl();
            if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
            return;
        }
        if( (apf_eeprom.lambda_dat_end - apf_eeprom.lambda_dat_beg) <= 10 )
        {
            uprintf("NAK,SPECTRA,EVal-SVal!>10: %d, %d", apf_eeprom.lambda_dat_beg, apf_eeprom.lambda_dat_end);  prn_nl();
            if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
            return;
        }
        uprintf("ACK,SPECTRA,%d,%d", apf_eeprom.lambda_dat_beg, apf_eeprom.lambda_dat_end); prn_nl();
        if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();

        result = waitACK(GET_ACK_TIMEOUT_MSEC);
        if(result)
        {
            fram_apfArray_refresh();      // if the array is updated, do nothing, otherwise update from fram

            if( apfArray.ZcoefsCnt <= 0 )
            {
                uprintf("NAK,SPECTRA,ZCoef File Error = %d", apfArray.ZcoefsCnt); prn_nl();
                if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
                return;
            }

            MatchPixelsToLambda(apfArray.LambdaData, apf_eeprom.lambda_dat_beg, apf_eeprom.lambda_dat_end, &apf_eeprom.isus_datpixel_beg, &apf_eeprom.isus_datpixel_end);

            if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();

            eeprom_apf_cfg_store();
            return;
        }
        else
        {
            uprintf("NAK,SPECTRA,ACK Failure"); prn_nl();
            LogErrorMsg("SPECTRA Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
        }
    }

    //uprintf("NAK,SPECTRA,ACK Failure"); prn_nl();
    //LogErrorMsg("SPECTRA Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);

    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();
    return;

}

void apf_spectra_get(void)
{
    // double beg;
    // double end;

    fram_apfArray_refresh();      // if the array is updated, do nothing, otherwise update from fram

//    beg = CellNumToLambda (apf_eeprom.isus_datpixel_beg);
//    end = CellNumToLambda (apf_eeprom.isus_datpixel_end);

    uprintf("ACK,SPECTRA?,%.1lf,%.1lf", apfArray.LambdaData[apf_eeprom.isus_datpixel_beg], apfArray.LambdaData[apf_eeprom.isus_datpixel_end]);  prn_nl();

    if(serRxFlushAfterCmdFlg == 1) UARTFlushRx();   //SCIRxFlush();
    return;

}

void apf_acq_time_get(void)
{

    if( apf.gotSample == TRUE )
    {
        uprintf("ACK,T,%.3f", apf.acquire_time); prn_nl();
    }
    else
    {
        uprintf("NAK,T,-1.0");  prn_nl();
    }
}

// "TINT"
#define MIN_TINT    50
#define MAX_TINT    4000
void apf_int_time_set(void)
{
    int32_t intTime;
    int     result;
    char *endptr;

    intTime = 0;

    char *str = (char *)&rx0.buf[0];

    // Tokenize the string using commas as delimiters
    strtok(str, ",");  // first token

    char *strptr = strtok(NULL, ",");  // 2nd  token
    if (strptr == NULL)
    {
        uprintf("NAK,TINT,FormatErr"); prn_nl();
        return;
    }

    errno = 0;
    intTime  = (uint32_t)strtol(strptr, &endptr, 10);  // string to signed long int
    if( errno == ERANGE )
    {
        uprintf("NAK,TINT,OverflowErr: %s", strptr); prn_nl();
        return;
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("NAK,TINT,ConvertErr: %s", strptr);  prn_nl();
        return;
    }
    else if ( (intTime >= MIN_TINT) && (intTime <= MAX_TINT) )          //range check
    {
        //apf_eeprom.isus_spec_period = intTime;
        uprintf("ACK,TINT,%u", intTime); prn_nl();
    }
    else
    {
        uprintf("NAK,TINT,RangeErr [%d,%d], %d", MIN_TINT, MAX_TINT, intTime); prn_nl();
        return;
    }

    result = waitACK(GET_ACK_TIMEOUT_MSEC);
    if(result)
    {
        apf_eeprom.isus_spec_period = intTime;
        //fram_apf_vars_store();
        eeprom_apf_cfg_store();
    }
    else
    {
        uprintf("NAK,TINT,ACK Failure"); prn_nl();
        LogErrorMsg("TINT Command ACK Failure", (int)COMMAND_ACK_FAILURE, apf_eeprom.sys_message_enable);
    }

}

void apf_int_time_get(void)
{
    uprintf("ACK,TINT?,%u", apf_eeprom.isus_spec_period); prn_nl();
}

// set human to machine h2m in seconds, H2M_DELIM_MS_TIMEOUT=1000
void apf_h2m_mode_set(uint32_t mult)
{
    //uprintf("ACK,H2M timeout %u ms", H2M_DELIM_MS_TIMEOUT);

    rx0.tick_ms_delim = H2M_DELIM_MS_TIMEOUT * mult;        // 1 sec

    //uprintf("H2M_MODE"); prn_nl();
    sys_data.terminal_mode = H2M_MODE;

    //turn echoflg on
    //uprintf("echo on"); prn_nl();
    apf_serial_term_echo(1, 0); //echo on, no prn

    sys_data.autoSleepSec = 30;
}

// get h2m time term in millisec
uint32_t apf_h2m_timeout_get(void)
{
    //uprintf("ACK,H2M timeout %u ms", H2M_DELIM_MS_TIMEOUT);

    return(rx0.tick_ms_delim);
}

void apf_m2m_mode_set(uint32_t termTimeMult)
{
    //uprintf("ACK,M2M timeout %u ms", M2M_DELIM_MS_TIMEOUT);
    if((termTimeMult >= 1) && (termTimeMult <= 9))
    {
        rx0.tick_ms_delim = M2M_DELIM_MS_TIMEOUT * termTimeMult;   // added slower termTime to aid testing m2m by hand
    }
    else
    {
        rx0.tick_ms_delim = M2M_DELIM_MS_TIMEOUT;
    }


    //uprintf("terminal mode = M2M_MODE"); prn_nl();
    sys_data.terminal_mode = M2M_MODE;

    //turn echoflg on
    //uprintf("echo on"); prn_nl();
    apf_serial_term_echo(0, 0); //echo off, dont print

    sys_data.autoSleepSec = 10;
}

// mode,1 command "C"
void apf_print_isus_header(void)
{
    FIL dat_file = { };

    //TODO open the file.

    //GH1
    //print, dont write
    print_write_isusdura_header(1, 0, ISUS_SENSOR_MODE, &dat_file);             //OutputIsusHeader(dat_file);

}
// mode,2 command "C"
void apf_print_dura_header(void)
{
    FIL dat_file = { };

    //TODO open the file.

    //GH2
    print_write_isusdura_header(1, 0, DURA_SENSOR_MODE, &dat_file);             //OutputDuraHeader(dat_file);
}

// T2 command
//msc1 cf2 TakeDuraSample()
void apf_take_sample_pH(int txFlg)
{
    uint32_t indx;
    uint16_t crc;

    // T2 command
    //int TakeDuraSample(char *dbuf, float *atime, ulong CTD_Time, float CTD_Temp, float CTD_Salinity, float CTD_Depth)
    //struct tm *time_struct;
    //char timestamp[TIMESTAMPLENGTH];
    //struct tm *time_struct;
    struct tm *tp;

    if(rx0.cmd == CMD_TAKESAMPLE)  // if we got here by way of "TS", else it must be T2 or S2
    {
        uprintf("ACK,TS,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_APF_DURA_T2)
    {
        uprintf("ACK,T2,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_APF_DURA_S2)
    {
        uprintf("ACK,S2,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_STARTSAMPLE)
    {
        uprintf("ACK,SS,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }

    get_sample(0,0);   // returns data populated in struct sampl

    if(rx0.cmd == CMD_TAKESAMPLE)
    {
        uprintf("ACK,TS,DAT");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_APF_DURA_S2)
    {
        uprintf("ACK,S2,DAT");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_STARTSAMPLE)
    {
        uprintf("ACK,SS,DAT");  prn_nl();         // cr vs crlf  defd in system.c
    }

    //sampl.timestamp = read_hib_rtcc();
    tp = get_RTC_time();
    //strftime(timestamp, sizeof(timestamp), "%m/%d/%Y %H:%M:%S", tp);

    // variable mapping from get_sample() sampl to apf vars

    apf_fram.dura_sample_ctr++;
    apf.sys_temperature = sampl.fBrdTempC;
    apf.sys_humidity = sampl.uiHumidity;
    apf.sys_current = sampl.fsysAmp;
    apf.sys_voltage = sampl.fVbat_main;
    apf.pH = sampl.pHext;
    apf.backupVoltage = sampl.fBiasPos;                    //what is this? APFTODO
    apf.VrsExt = sampl.fVrse;
    apf.StdDevVrs = sampl.fVrse_std;
    apf.Vk = sampl.fVk;
    apf.StdDevVk = sampl.fVk_std;
    apf.Ik = sampl.fIk;
    apf.Ib = sampl.fIb;

    //zero the prnBuf, trying to fix the bug where the sprintf string gets chopped off
    for(indx=0; indx<300; indx++)       //300 is larger than the 165 chars of T2 output
        prnBuf[indx] = 0;

#define CRC_PRNSTRING_OFFSET    7
    for(indx=0; indx<CRC_PRNSTRING_OFFSET; indx++)
        prnBuf[indx] = ' ';

    indx = CRC_PRNSTRING_OFFSET;  //7
                                                                     //      1    2    3     4    5    6    7   8    9   10   11  12  13  14  15   16   17   18   19   20   21 22 23  24  25
  //indx += (unsigned int)sprintf((char*)&prnBuf[CRC_PRNSTRING_OFFSET], "pH,%02d/%02d/%02d %02d:%02d:%02d,%lu,%.2f,%.4f,%.4f,%lu,%lu,%lu,%.2f,%.2f,%.3f,%.3f,%.4f,%.3f,%+f,%f,%+f,%f,%+.3e,%+.3e",
    indx += (unsigned int)sprintf((char*)&prnBuf[CRC_PRNSTRING_OFFSET], "pH,%02d/%02d/%02d %02d:%02d:%02d,%lu,%.2f,%.4f,%.4f,%lu,%lu,%lu,%.2f,%u,%.3f,%.3e,%.4f,%.3f,%+f,%f,%+f,%f,%.3f,%.3f",
            tp->tm_mon+1, tp->tm_mday, tp->tm_year+1900, tp->tm_hour, tp->tm_min, tp->tm_sec,   //1 2 3 4 5 6
            apf.CTD_Time, apf.CTD_Depth, apf.CTD_Temp, apf.CTD_Salinity,                        //7 8 9 10
            apf_fram.dura_sample_ctr, apf_fram.sys_wdog_ctr, apf_fram.dura_error_ctr,                           //11 12 13
            apf.sys_temperature, apf.sys_humidity, apf.sys_voltage, apf.sys_current,   //14 15 16 17
            apf.pH, apf.backupVoltage, apf.VrsExt, apf.StdDevVrs, apf.Vk, apf.StdDevVk, apf.Ik, apf.Ib); //18 19 20 21 22 23 24 25

    prnBuf[indx] = 0;
    indx++;

    crc = fCrc16Bit((const unsigned char *)prnBuf + CRC_PRNSTRING_OFFSET);
    sprintf((char*)&prnBuf[0],"0x%04X", crc);
    prnBuf[6] = ',';

    //prnBuf holds the sample for send last "SL"
    apf_fram.sendlast_byteCnt = strlen((const char*)prnBuf);
    fram_apf_dbuf_store(&prnBuf[0], apf_fram.sendlast_byteCnt);     //store for SL send last

    // save persistent info to FRAM
    fram_apf_vars_store();

    if(txFlg > 0)
    {
        uprintf("%s", prnBuf); prn_nl();        //print to console
    }

	prnBuf[apf_fram.sendlast_byteCnt] = '\r';
	prnBuf[apf_fram.sendlast_byteCnt+1] = '\n';
	prnBuf[apf_fram.sendlast_byteCnt+2] = 0x00;

    appendDuraProfileDataToSDCard((char*)prnBuf);
}


float apf_pH(void)
{
    get_sample(0,0);

    apf.dura_pH = sampl.pHext;

    uprintf("%.2lf", apf.dura_pH); prn_nl();

    return((float)apf.dura_pH);
}


// apf_take_sample_nitrate, invoked by "T1" and "S1" and "TS" (mode=1), command, calls TakeIsusSample / mms_takeIsusSample
// txFlg = 0 'start sample' but don't send, use "sl" to send, txFlg = 1 is take sample and send
void apf_take_sample_nitrate(int txFlg)
{
    int     ret;

    if(mmsDbgFlg == 1) { uprintf("Dev in progress on apf_take_sample_nitrate, calls mms_takeIsusSample"); prn_nl(); }
    //cmd T1 TakeIsusSample

    if(rx0.cmd == CMD_TAKESAMPLE)  // if we got here by way of "TS", else it must be T1 or S1
    {
        uprintf("ACK,TS,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_APF_ISUS_T1)
    {
        uprintf("ACK,T1,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_APF_ISUS_S1)
    {
        uprintf("ACK,S1,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }
    if(rx0.cmd == CMD_STARTSAMPLE)
    {
        uprintf("ACK,SS,CMD");  prn_nl();         // cr vs crlf  defd in system.c
    }

    ret = mms_takeIsusSample(&apf.acquire_time, apf.CTD_Time, apf.CTD_Temp, apf.CTD_Salinity, apf.CTD_Depth, txFlg);     //defd in mms_spec.c

    if( ret == OK )
    {
        if(txFlg == 0)
        {
            if(rx0.cmd == CMD_TAKESAMPLE)  // if we got here by way of "TS", else it must be T1 or S1
            {
                uprintf("ACK,TS,DAT");  prn_nl();         // cr vs crlf  defd in system.c
            }
#ifdef HANDLEDBYTXFLG
            if(rx0.cmd == CMD_APF_ISUS_T1)
            {
                uprintf("ACK,T1,DAT");  prn_nl();         // cr vs crlf  defd in system.c
            }
#endif
            if(rx0.cmd == CMD_APF_ISUS_S1)
            {
                uprintf("ACK,S1,DAT");  prn_nl();         // cr vs crlf  defd in system.c
            }
            if(rx0.cmd == CMD_STARTSAMPLE)
            {
                uprintf("ACK,SS,DAT");  prn_nl();         // cr vs crlf  defd in system.c
            }

        }
        apf.gotSample = TRUE;
    }
    else
    {
        //if takeIsusSample failed...

        if(rx0.cmd == CMD_TAKESAMPLE)  // if we got here by way of "TS", else it must be T1 or S1
        {
            uprintf("NAK,TS,DAT");  prn_nl();         // cr vs crlf  defd in system.c
        }
        if(rx0.cmd == CMD_APF_ISUS_T1)
        {
            uprintf("NAK,T1,CMD");  prn_nl();         // cr vs crlf  defd in system.c
        }
        if(rx0.cmd == CMD_APF_ISUS_S1)
        {
            uprintf("NAK,S1,CMD");  prn_nl();         // cr vs crlf  defd in system.c
        }
        if(rx0.cmd == CMD_STARTSAMPLE)
        {
            uprintf("NAK,SS,CMD");  prn_nl();         // cr vs crlf  defd in system.c
        }

        apf.gotSample = FALSE;
    }

    mms_flushRx();   //  was SCIRxFlush();

}

float apf_nitrate(void)
{
    int retVal;

    retVal = mms_takeIsusSample(&apf.acquire_time, apf.CTD_Time, apf.CTD_Temp, apf.CTD_Salinity, apf.CTD_Depth, 0);

    if (retVal != OK)
    {
        uprintf("mms_takeIsusSample returned ERROR!");   //NO3 command is not part of MSC Interface spec, ok to have error message here
        prn_nl();
    }

    uprintf("No3: %.2lf, Sal: %.2lf", apf.isus_no3, apf.isus_sal); prn_nl();

    return((float)apf.isus_no3);
}


//--------- End of APF float commands (thank god) ---------//

void print_cmd_version(void)
{
    uprintf(VERSION);
    prn_nl();
}

void cmd_period_get(void)
{
    uprintf("period=%u", sys_data.sampling_period); prn_nl();
}

void cmd_period_set(void)
{
    int indx;
    int st;
    int spaceFlg;
    unsigned int period;

    spaceFlg = 0;
    // period=1000 period,1000 period 1000
    for(indx=3; indx<rx0.indx; indx++)
    {
        if( (rx0.buf[indx] == ' ') || (rx0.buf[indx] == ',') || (rx0.buf[indx] == '='))       // find first space comma or equal after 'period'
        {
            spaceFlg = 1;
            //uprintf("time: %s", &rx0.buf[indx]);     // DEBUG
        }
        if( (spaceFlg == 1) && ( (rx0.buf[indx] >= '1') && (rx0.buf[indx] <= '9') ) )   //find first number after space
        {
            break;
        }
    }

    st = indx;
    if(sscanf((char*)&rx0.buf[st], "%u", &period) == 1)
    {
        // sample_period = 0 means POLLED MODE

        if(period == 0)
        {
            //uprintf("Sample period is zero - polled mode"); prn_nl();
        }


        if(sys_data.sample_aligned)     // Sample aligned to hour?
        {
            if((3600 % period) && (period > 0))     // BUG Fix 6 May 2019, per request to allow sample aligned to hour in polled mode (sample period = 0) - Not sure the sense of it though since in polled mode, the poller is determining the time
            {
                prn_nl(); uprintf("Aligned=Y, sample period must evenly divide hour (e.g. 5, 10, 15, 20, 30 minutes)");
                return;
            }
        }

        if(period <= MAX_SAMPLING_PERIOD)        // Check if less than hour 3600 seconds
        {
            sys_data.sampling_period = period;
            eeprom_sys_data_store();
            cmd_period_get();
        }
        else
        {
            prn_nl(); uprintf("Enter time less than or equal to %u sec", MAX_SAMPLING_PERIOD);
        }

    }
    else
    {
        prn_nl(); uprintf("Enter seconds greater than or equal to 0 and less than 3600"); prn_nl();
    }

}

void cmd_time_help(void)
{
    prn_nl();
    uprintf("  Set:  time MM/DD/YY HH:MM:SS"); prn_nl();
    uprintf("  Set:  time 11/07/23 13:45:30"); prn_nl();
    uprintf(" Read:  time"); prn_nl();
    uprintf(" Read:  time,?"); prn_nl();
    uprintf(" Help:  time,h"); prn_nl();        //this
}

unsigned int cmd_time_get(void)
{
    unsigned int ticks;

    ticks = read_hib_rtcc();
    print_time_t_Time(ticks);

    return(ticks);
}

/**
 * Set time from rx0.buf input string
 * Format mm/dd/yyyy hh:mm:ss
 */
void cmd_time_set(void)
{
    int indx;
    int st;
    int month;
    int day;
    int year;
    int hour;
    int minute;
    int second;

    //time variables:
    time_t rawtime;
    struct tm * timeStruct;

    for(indx=4; indx<rx0.indx; indx++)
    {
        if( (rx0.buf[indx] == ' ') || (rx0.buf[indx] == ',') || (rx0.buf[indx] == '='))       // find valid char after 'time' or 'settime'
        {
            // if formatted for time
            st = indx+1;      // start of time string
            int items = sscanf((char*)&rx0.buf[st], "%d/%d/%d %d:%d:%d", &month, &day, &year, &hour, &minute, &second);
            if (items == 6)
            {
                if( second >= 0 && minute >= 0 && hour >= 0 && month >= 1 && day >= 1 && year >= 25)
                {
                    if( second <= 59 && minute <= 59 && hour <= 23 && day <= 31 && month <= 12)
                    {
                        //fill in time.h's tm struct initially (timeStruct just points to time.h's struct)
                        time(&rawtime);
                        //timeStruct = localtime(&rawtime);     //mod 31 Mar 2025
                        timeStruct = get_RTC_time();                           //populate time struct

                        //modify as user desired
                        timeStruct->tm_sec = second;
                        timeStruct->tm_min = minute;
                        timeStruct->tm_hour = hour;
                        timeStruct->tm_mday = day;
                        timeStruct->tm_mon = month - 1;
                        timeStruct->tm_year = year + 100; //  1900;

                        //GMT HAS NO DAYLIGHT SAVINGS
                        timeStruct->tm_isdst = 0;

                        //remake the struct
                        rawtime = mktime(timeStruct);

                        MAP_HibernateRTCSet(rawtime); // Set time if user entered valid time

                        printCurrentTime(); prn_nl();

                        ds3231_set_time();   //program the RTC on the baseboard
                        return;
                    }
                }
            }
            break;
        }
    }

    print_NAK();

}

void cmd_get_filename(void)
{
    uprintf("%s", sys_data.fileName); prn_nl();
}

void cmd_set_filename(void)
{
    int indx;
    int st;
    int name_len;
    int spaceFlg;

    spaceFlg = 0;
    for(indx=0; indx<rx0.indx; indx++)
    {
        if( (spaceFlg == 0) && (rx0.buf[indx] == ' ') )       // find first space
        {
            spaceFlg = 1;
            //uprintf("filename: %s", &rx0.buf[indx]);     // DEBUG
        }


        if( (spaceFlg == 1) && ( (rx0.buf[indx] >= 'a') && (rx0.buf[indx] <= 'z') ) )   //find first alpha after space
        {
            st = indx;      // buf indx

            name_len = strlen((const char*)&rx0.buf[st]);

            if (name_len > 12)
            {
              uprintf("filename is too long, needs to be 8.3 format"); prn_nl();
              break;
            }

            for(indx=st; indx<rx0.indx; indx++)
            {
                if(rx0.buf[indx] == '.')
                {
                    rx0.buf[indx+4] = 0;        // trim ext to 3 chars
                }
            }


            strcpy(sys_data.fileName, (char*)&rx0.buf[st]);

            eeprom_sys_data_store();    //save
            //uprintf("filename: %s", sys_data.fileName);     // DEBUG
            break;
        }
    }

    //find first space after file..., then find first alpha char
}

void cmd_get_vrsi_std(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_VRSI_STD]);
}

void cmd_set_vrsi_std(unsigned int val)
{
    if((val == '1') || (val == 1))
    {
        sys_data.data_cfg[CFG_VRSI_STD] = 1;
        sys_data.data_cfg[CFG_VRSI] = 1;
    }
    else
        sys_data.data_cfg[CFG_VRSI_STD] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vrsi_std(unsigned int num)
{
    // min num is 5
    //uprintf("Not available");
    // TODO, parse trials, for now, set to 5
    test_vrsi(num, 1, 5, 0, 500);
}

void cmd_get_vrsi(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_VRSI]);
}

void cmd_set_vrsi(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_VRSI] = 1;
    else
        sys_data.data_cfg[CFG_VRSI] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vrsi(unsigned int num)
{
    //uprintf("Not available");
    test_vrsi(num, 0, 0, 0, 500);
}

void cmd_get_vrse_std(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_VRSE_STD]);
}

void cmd_set_vrse_std(unsigned int val)
{
    if((val == '1') || (val == 1))
    {
        sys_data.data_cfg[CFG_VRSE_STD] = 1;
        sys_data.data_cfg[CFG_VRSE] = 1;
    }
    else
        sys_data.data_cfg[CFG_VRSE_STD] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vrse_std(unsigned int num)
{
    uint32_t indx;

    //APFTODO
    num = 5;

    //uprintf("Not available");
    // TODO, parse trials, for now, set to 10
    for(indx=0; indx<num; indx++)
    {
        test_vrse(num, 1, 10, 0, 500);
        //look for key and abort
        if(kbhit())
            break;
    }

}

void cmd_vrse_std_help(void)
{
    uprintf(""); prn_nl();
}

void cmd_get_vrse(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_VRSE]);
}

void cmd_set_vrse(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_VRSE] = 1;
    else
        sys_data.data_cfg[CFG_VRSE] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vrse(unsigned int num)
{
    uint32_t indx;

    //APFTODO
    num = 5;

    //uprintf("Not available");
    // TODO, parse trials, for now, set to 10
    for(indx=0; indx<num; indx++)
    {
        test_vrse(num, 0, 0, 0, 500);
        //look for key and abort
        if(kbhit())
            break;
    }

}

void cmd_get_ib(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_I_SUBSTRATE]);
}

void cmd_set_ib(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_I_SUBSTRATE] = 1;
    else
        sys_data.data_cfg[CFG_I_SUBSTRATE] = 0;

    eeprom_sys_data_store();
}

void cmd_test_ib(unsigned int num)
{
    //uprintf("Not available");
    test_ib(num, 0, 0, 0, 500);
}

void cmd_get_ik(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_I_COUNTER]);
}

void cmd_set_ik(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_I_COUNTER] = 1;
    else
        sys_data.data_cfg[CFG_I_COUNTER] = 0;

    eeprom_sys_data_store();
}

void cmd_test_ik(unsigned int num)
{
    //uprintf("Not available");
    //void test_ik(unsigned int num, unsigned int stdFlg, unsigned int trials, unsigned int sps, unsigned int msec)
    test_ik(num, 0, 0, 0, 500);     // defd in test.c
}


void cmd_get_vk(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_V_COUNTER_ELECT]);
}

void cmd_set_vk(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_V_COUNTER_ELECT] = 1;
    else
        sys_data.data_cfg[CFG_V_COUNTER_ELECT] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vk(unsigned int num)
{
    //uprintf("Not available");
    test_vk(num, 0, 0, 0, 500);
}

void cmd_get_vk_std(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_V_COUNTER_ELECT_STD]);
}

void cmd_set_vk_std(unsigned int val)
{
    if((val == '1') || (val == 1))
    {
        sys_data.data_cfg[CFG_V_COUNTER_ELECT_STD] = 1;
        sys_data.data_cfg[CFG_V_COUNTER_ELECT_STD] = 1;
    }
    else
        sys_data.data_cfg[CFG_V_COUNTER_ELECT_STD] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vk_std(unsigned int num)
{
    //uprintf("Not available");
    test_vk(num, 1, 5, 0, 500);

}

void cmd_get_vtherm(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_V_THERMISTOR]);
}

void cmd_set_vtherm(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_V_THERMISTOR] = 1;
    else
        sys_data.data_cfg[CFG_V_THERMISTOR] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vtherm(unsigned int num)
{
    test_vtherm(num, 0, 0, 0, 500);
}

void cmd_get_vtherm_std(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_V_THERMISTOR_STD]);
}

void cmd_set_vtherm_std(unsigned int val)
{
    if((val == '1') || (val == 1))
    {
        sys_data.data_cfg[CFG_V_THERMISTOR] = 1;
        sys_data.data_cfg[CFG_V_THERMISTOR_STD] = 1;
    }
    else
        sys_data.data_cfg[CFG_V_THERMISTOR_STD] = 0;

    eeprom_sys_data_store();
}

void cmd_test_vtherm_std(unsigned int num)
{
    test_vtherm(num, 1, 5, 0, 500);
}


void cmd_get_tempc(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_CALC_TEMP]);
}

void cmd_set_tempc(unsigned int val)
{
    if((val == '1') || (val == 1))
    {
        sys_data.data_cfg[CFG_V_THERMISTOR] = 1;
        sys_data.data_cfg[CFG_CALC_TEMP] = 1;
    }
    else
        sys_data.data_cfg[CFG_CALC_TEMP] = 0;

    eeprom_sys_data_store();
}

void cmd_test_tempc(unsigned int num)
{
    test_tempc(num, 0, 0, 0, 500);   // stdFlg will print vtherm and vtherm_std
}

void cmd_test_vbat(unsigned int num)
{
    //uprintf("Not available");
    display_battery_boardSensors();
}

void cmd_get_vbat(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_VIN_BAT_VOLT]);
}

void cmd_set_vbat(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_VIN_BAT_VOLT] = 1;
    else
        sys_data.data_cfg[CFG_VIN_BAT_VOLT] = 0;

    eeprom_sys_data_store();
}

void cmd_test_biasneg(unsigned int num)
{
    //uprintf("Not available");
    test_bias_bat(num, 1, 0, 0);

}

void cmd_get_biasneg(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_BIAS_BAT_NEG]);
}

void cmd_set_biasneg(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_BIAS_BAT_NEG] = 1;
    else
        sys_data.data_cfg[CFG_BIAS_BAT_NEG] = 0;

    eeprom_sys_data_store();
}

void cmd_test_biaspos(unsigned int num)
{
    //uprintf("Not available");
    test_bias_bat(num, 0, 1, 0);

}

void cmd_get_biaspos(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_BIAS_BAT_POS]);
}

void cmd_set_biaspos(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_BIAS_BAT_POS] = 1;
    else
        sys_data.data_cfg[CFG_BIAS_BAT_POS] = 0;

    eeprom_sys_data_store();
}


void cmd_timestamp_get(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_TIMESTAMP]);

}

void cmd_timestamp_set(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_TIMESTAMP] = 1;
    else     if((val == '2') || (val == 2))
        sys_data.data_cfg[CFG_TIMESTAMP] = 2;
    else
        sys_data.data_cfg[CFG_TIMESTAMP] = 0;

    eeprom_sys_data_store();
}

void cmd_timestamp_test(unsigned int num)
{
    char timeStamp[TIMESTAMPLENGTH];
    struct tm *time_struct;
    time_t current_time;
    unsigned long long posixtime_ms;

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

        current_time = read_hib_rtcc();
        posixtime_ms = (long long)current_time;     // was read_hib_rtcc();   // cast to unsigned long long ?

        posixtime_ms -= TI_CCS_UNIX_EPOCH_OFFSET;

        posixtime_ms *= 1000LL;      // make it msec
        sprintf(timeStamp, "%13llu", posixtime_ms);
        uprintf("%s", timeStamp);
    }

    if(sys_data.data_cfg[CFG_TIMESTAMP] == TYPE_US_DATETIME)
    {
        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
        uprintf("%s", timeStamp);

    }

}

void cmd_sampfast_help(void)
{
    uprintf("sampfast            - fast ISUS sampling every 1 seconds, with 10 light scans in between dark scans (default params)"); prn_nl();
    uprintf("sampfast,v          - same as above but 'verbose mode' also outputs all data to screen in addition to writing to file"); prn_nl();
    uprintf("sampfast,[v],2,20   - sample every 2 seconds, with 20 light scans in between dark scans"); prn_nl();
    prn_nl();
    uprintf("sampfast always writes to currently set profile file.  Get and set profile number using 'profile' commands."); prn_nl();
    uprintf("Lamp stays on in between dark scans.  Use 'scanspectra' to turn lamp off between all scans."); prn_nl();
}

void cmd_cfg_sampnum_get(void)
{
    uprintf("%u", sys_data.data_cfg[CFG_SAMPLE_NUM]);
}

void cmd_cfg_sampnum_set(unsigned int val)
{
    if((val == '1') || (val == 1))
        sys_data.data_cfg[CFG_SAMPLE_NUM] = 1;
    else
        sys_data.data_cfg[CFG_SAMPLE_NUM] = 0;

    eeprom_sys_data_store();
}

void cmd_sampnum_set(unsigned int num)
{
    sys_samp.current_sample = num;
    uprintf("#%07u", sys_samp.current_sample); prn_nl();

    eeprom_sys_data_store();
}

void cmd_sampnum_test(unsigned int num)
{
    //sys_samp.current_sample
    uprintf("#%07u", sys_samp.current_sample); prn_nl();
}

void cmd_config_help(void)
{
    uprintf("cmd_config_help Imp Es Nada");   prn_nl();
}

void cmd_config_get(void)
{
    print_config();
}

void cmd_config_set(void)
{
#ifdef NOCODE
    0 -- Zero (Clear) all config fields
    1 -- Set all config fields
    2 -- Use Case: GLIDER,          (A,B,C,D,L,M,R,T,U)
    3 -- Use Case: mFET Default,    (A,B,C,D,E,F,G,H,I,J,K,R,T,U)
    4 -- Use Case: MpHOx Default,   (A,B,C,D,F,G,H,I,L,M,R,T,U,V,W)
    5 -- Use Case: MpHOx m2m,       (A,C,D,E,F,G,H,I,J,K,N,R,T,U)
    6 -- Use Case: nanoFET Default, (A,B,D,F,G,H,I,L,R,S,T,U)
    7 -- Use Case: MFET WireWalker, (B,G,L,R,T,U)
#endif

    if( (strncmp((const char*)&rx0.buf[7], "glider", 4) == 0)  )
    {
        config_data_fields_glider(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "wirewalker", 6) == 0)  )
    {
        config_data_fields_wirewalker(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "mfet", 4) == 0)  )
    {
        config_data_fields_mfet(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "mphox", 5) == 0)  )
    {
        config_data_fields_mphox(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "m2m mphox", 8) == 0)  )
    {
        config_data_fields_m2m_mphox(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "nanofet", 4) == 0)  )
    {
        config_data_fields_nanofet(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "all", 3) == 0) || (rx0.buf[7] == '1') )
    {
        config_data_fields_all(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "none", 4) == 0)  || (rx0.buf[7] == '0') )
    {
        config_data_fields_none(0);
    }
    else if ( (strncmp((const char*)&rx0.buf[7], "test", 4) == 0)  )
    {
        uprintf("not available");  prn_nl();
    }
    else
    {
        print_NAK();
    }
}

void cmd_store_get(void)
{
    uprintf("microsd=%u", sys_data.app_cfg[APPCFG_MICROSD_ENABLE]);
    //sys_data.app_cfg[APPCFG_MICROSD_ENABLE] = 1;
}


// 'store 1' or 'microsd 1'
void cmd_store_set(void)
{
    unsigned char oldVal;

    oldVal = sys_data.app_cfg[APPCFG_MICROSD_ENABLE];
    //sys_data.app_cfg[APPCFG_MICROSD_ENABLE] = 1;

    if((rx0.buf[6] == '1') || (rx0.buf[8] == '1'))     // store 1  or // microsd 1
    {
        sys_data.app_cfg[APPCFG_MICROSD_ENABLE] = 1;

        if(oldVal == 0)
        {
            // init the filesystem
            if( uSD_present() )
            {
                prn_nl();
                uprintf("MicroSD card is Initialized...."); prn_nl();
                init_uSDCard();     //defd in microsd.c
                microSD_flg = 1;
            }
            else
            {
                prn_nl();
                uprintf("Error: MicroSD card is not detected"); prn_nl();
                microSD_flg = 0;
            }
        }


    }

    if((rx0.buf[6] == '0') || (rx0.buf[8] == '0'))     // store 1  or // microsd 1
    {
        sys_data.app_cfg[APPCFG_MICROSD_ENABLE] = 0;
        microSD_flg = 0;
        uprintf("MicroSD card will be disabled after sleep / wakeup"); prn_nl();
    }

    cmd_store_get();
    eeprom_sys_data_store();
}

void cmd_filesystem(void)
{
    UARTEchoSet(true);
#if BOARD_MFET >= 2 || BOARD_MPHOX >= 1
    fVbat = read_vbat();     //  print a warning if Vin is too low, especially for ymodem transfers  Bug fix 11 Mar 2022
    if( fVbat < (sys_data.low_batt_volt + 0.005) )
    {
        prn_nl(); prn_nl(); uprintf("Voltage input is low (%.2fv < threshold %.2fv, avoid file operations!!!", fVbat, sys_data.low_batt_volt); prn_nl();
    }

    if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] == 0)   // Fix 6 Aug 2021
    {
        prn_nl();
        uprintf("MicroSD card disabled, file commands are not available"); prn_nl();
        uprintf("   To enable, use 1-Config -> A-App Config to edit app_cfg[APPCFG_MICROSD_ENABLE]=1"); prn_nl();
    }
    else
    {
        if(microSD_flg == 1)
        {
            viewFiles();
        }
        else
        {
            prn_nl(); uprintf("MicroSD card not detected, file commands are not available"); prn_nl();
        }
    }
#endif
#if BOARD_NANOFET > 0
    uprintf("MicroSD card is not available on NanoFET, file commands are not available"); prn_nl();
#endif

    UARTEchoSet(false);
}

// pass in sample loopTime in seconds (APFTODO, makes this 0 to 3600 instead of 0 to 9)
// APFTODO use 'mode' to guide which samples run
void cmd_test_sample(unsigned int loopTime)
{

    //uprintf("DBG: barf = %u, loopTIme = %u", databarFlg, loopTime); prn_nl();

    if(databarFlg == 1)
    {
        barfSampleTime = 5;
        databarFlg = 0;
        if(apf_fram.sys_sensor_mode == 3)
        {
            apf_fram.sys_sensor_mode = 1;    // ISUS
            fram_apf_vars_store();
        }
    }
    else
    {
        if (loopTime > 3600)
            loopTime = 5;
        barfSampleTime = loopTime;
        databarFlg = 1;
    }

        // continuous sampling at loopTime (secs)
}


void apf_baud_get(void)
{
    uprintf("baud=%u", sys_data.baud_rate);  prn_nl();
    wait_consoleTx();
}


uint32_t apf_baud_set(void)
{
    uint32_t baudRate;
    int      result;
    uint32_t indx;
    char     *str;
    char     *endptr;

    str = (char*)&rx0.buf[5];

    errno = 0; // Reset errno before calling strtoul
    baudRate = strtoul(str, &endptr, 10);

    if (errno == ERANGE)
    {
        uprintf("Error: Baud value out of range");  prn_nl();
        return(sys_data.baud_rate);     // return old baud rate
    }
    else if (errno == EINVAL)
    {
        uprintf("Error: No conversion possible");  prn_nl();
        return(sys_data.baud_rate);     // return old baud rate
    }
    else
    {
        //uprintf("DBG: Converted number: %lu", baudRate);  prn_nl();
        //uprintf("DBG: String after number: %s", endptr);   prn_nl(); prn_nl();
       // uprintf("Change serial terminal (minicom, TeraTerm, etc) baud rate, then type ACK"); prn_nl();
        //uprintf("If is ACK not received in 10 sec, reverts to previous baud"); prn_nl();
        //wait_consoleTx();
        //delay_msec(1000);
    }

    switch(baudRate)
    {
        //case 921600:   //wake circuit does not respond consistently
       //     break;
        case 460800:
            break;
        case 230400:
            break;
        case 115200:
            break;
        case 57600:
            break;
        case 38400:
            break;
        case 19200:
            break;
        case 9600:
            break;
        case 4800:
            break;
        default:
            uprintf("NAK,BAUD,%u", baudRate); prn_nl();
            return(sys_data.baud_rate);     // return old baud rate
#ifdef WARNING_ABATEMENT_UNREACHABLE
            break;
#endif

    }

    uprintf("Change serial terminal baud rate (minicom, TeraTerm, etc), then type ACK"); prn_nl();
    uprintf("If ACK is not received in 10 sec at the new rate, MoSoS reverts to previous baud"); prn_nl();

    wait_consoleTx();
    UARTFlushRx();   //clear junk from baud change
    UARTStdioConfig(0, baudRate, 16000000);     //change to new baud rate
    delay_msec(10);
    UARTFlushRx();  // clear junk from baud change

#define BAUDRETRY      5
    for(indx=0; indx<BAUDRETRY; indx++)
    {
        result = waitACK(4000);
        if(result)
        {
            sys_data.baud_rate = baudRate;
            uprintf("baudrate = %u", sys_data.baud_rate);  prn_nl();
            // Write the sys_data struct into EEPROM.  Last argument ensures # of bytes is multiple of 4
            if(eeprom_sys_data_store())                 //if(EEPROMProgram((uint32_t*) &sys_data, 0x400, (sizeof(sys_data) + 3) & ~3))
            {
                prn_nl(); uprintf("Error storing system data."); prn_nl();
            }
            else
            {
                prn_nl(); uprintf("System data stored..."); prn_nl();
            }

            return(sys_data.baud_rate);
        }
        else
        {
            //typo or timeout
        }

        uprintf("Try again %u, type ACK for baud = %u", (BAUDRETRY-indx), baudRate); prn_nl();
    }

    wait_consoleTx();
    UARTFlushRx();
    UARTStdioConfig(0, sys_data.baud_rate, 16000000);     //change to new baud rate
    delay_msec(10);
    UARTFlushRx();
    uprintf("Baud restored to %u", sys_data.baud_rate); prn_nl();
    wait_consoleTx();

    return(sys_data.baud_rate);
}

void apf_baud_help(void)
{
    uprintf("Valid baud rate options:   (Note: 230k, 460k are experimental, 921k does not wake on serial");  prn_nl();
    uprintf("baud,4800");    prn_nl();
    uprintf("baud,9600");    prn_nl();
    uprintf("baud,19200");   prn_nl();
    uprintf("baud,38400");   prn_nl();
    uprintf("baud,57600");   prn_nl();
    uprintf("baud,115200");  prn_nl();
    uprintf("baud,230400");  prn_nl();
    uprintf("baud,460800");  prn_nl();          //this baud rate exceeds capabilities of rs232 transceiver, but seems to work fine
    //uprintf("baud,921600");  prn_nl();        //MSC3 revD baseboard does not wake at this baud rate
}

//"sernum"
uint32_t apf_sernum_msc_get(void)
{
    uprintf("sernum,msc=%04u", apf_eeprom.sys_serial_num); prn_nl();
    return(apf_eeprom.sys_serial_num);
}

void apf_sernum_msc_help(void)
{
    uprintf("sernum,msc          - print msc serial number [0001 : 9999]"); prn_nl();
    uprintf("sernum,msc,?        - print msc serial number [0001 : 9999]"); prn_nl();
    uprintf("sernum,msc,1234     - set msc serial number 1234 [0001 : 9999]"); prn_nl();
    uprintf("sernum,msc=1234     - set msc serial number 1234 [0001 : 9999]"); prn_nl();
}

void apf_sernum_help(void)
{

    uprintf("sernum              - print msc serial number [0001 : 9999]"); prn_nl();
    uprintf("sernum,?            - print msc serial number [0001 : 9999]"); prn_nl();
    uprintf("sernum,1234         - set msc serial number 1234 [0001 : 9999]"); prn_nl();
    uprintf("sernum=1234         - set msc serial number 1234 [0001 : 9999]"); prn_nl();
    prn_nl();
    uprintf("sernum,base         - print baseboard serial number, %d characters", MAX_BOARD_SN-1); prn_nl();  //baseboard
    uprintf("sernum,ctd          - print ctd serial number, %d characters", MAX_CTD_SN-1); prn_nl();
    uprintf("sernum,isfet        - print isfet serial number, %d characters", MAX_ISFET_SN-1); prn_nl();
    uprintf("sernum,ise          - print ise serial number, %d characters", MAX_ISE_SN-1); prn_nl();
    uprintf("sernum,lamp         - print lamp serial number, %d characters", MAX_LAMP_SN-1); prn_nl();
    uprintf("sernum,mcap         - print mcap serial number, %d characters", MAX_MCAP_SN-1); prn_nl();
    uprintf("sernum,msc          - print msc serial number, %d characters", 4); prn_nl();
    uprintf("sernum,optode       - print optode serial number, %d characters", MAX_OPTODE_SN-1); prn_nl();
    uprintf("sernum,phsensor     - print pH sensor serial number, %d characters", MAX_SENSOR_SN-1); prn_nl();
    uprintf("sernum,probe        - print probe serial number, %d characters", MAX_PROBE_SN-1); prn_nl();
    uprintf("sernum,spec         - print spec serial number, %d characters", MAX_SPEC_SN-1); prn_nl();
    uprintf("sernum,spec_analog  - print spec analog serial number, %d characters", MAX_SPEC_ANALOG_SN-1); prn_nl();
    uprintf("sernum,spec_digital - print spec digital serial number, %d characters", MAX_SPEC_DIGITAL_SN-1); prn_nl();
    uprintf("sernum,tiva         - print tiva board serial number, %d characters", MAX_TIVA_SN-1); prn_nl();


    prn_nl();
}



uint32_t apf_sernum_msc_set(void)
{
    uint32_t serNum;
    char *endptr;
    char *str;

    str = (char*)&rx0.buf[7];

    errno = 0; // Reset errno before calling strtoul
    serNum = strtoul(str, &endptr, 10);

    if(serNum > 0 && serNum <= 9999)
    {
        apf_eeprom.sys_serial_num = serNum;
        uprintf("sernum,msc=%04u", apf_eeprom.sys_serial_num); prn_nl();
        eeprom_apf_cfg_store();
        //fram_apf_vars_store();
    }
    else
    {
        uprintf("serial number %u, out of range [0001 : 9999]", serNum); prn_nl();
    }

    return(serNum);
}

//board_SN[MAX_BOARD_SN]
void apf_sernum_baseboard_get(void)
{
    uprintf("sernum,base=%s", sys_data.board_SN); prn_nl();
}

void apf_sernum_baseboard_help(void)
{
    uprintf("sernum,base=[%u chars]", MAX_BOARD_SN-1); prn_nl();
}
void apf_sernum_baseboard_set(void)
{
    //0123456789012345678
    //sernum,base,
    rx0.buf[12+MAX_BOARD_SN-1] = 0x00;
    sprintf(sys_data.board_SN, "%s", &rx0.buf[12]);

    //uprintf("%s %u", sys_data.board_SN, (uint32_t)strlen(sys_data.board_SN)); prn_nl();
    uprintf("sernum,base=%s", sys_data.board_SN); prn_nl();  //Should this be board_SN= ???
    eeprom_sys_data_store();
}

//ctd_SN[MAX_CTD_SN]
void apf_sernum_ctd_get(void)
{
    uprintf("sernum,ctd=%s", sys_data.ctd_SN); prn_nl();
}

void apf_sernum_ctd_help(void)
{
    uprintf("sernum,ctd=[%u chars]", MAX_CTD_SN-1); prn_nl();
}
void apf_sernum_ctd_set(void)
{
    //0123456789012345678
    //sernum,ctd,
    rx0.buf[11+MAX_CTD_SN-1] = 0x00;
    sprintf(sys_data.ctd_SN, "%s", &rx0.buf[11]);

    uprintf("sernum,ctd=%s", sys_data.ctd_SN); prn_nl();
    eeprom_sys_data_store();
}

//isfet_SN[MAX_ISFET_SN]
void apf_sernum_isfet_get(void)
{
    uprintf("sernum,isfet=%s", sys_data.isfet_SN); prn_nl();
}

void apf_sernum_isfet_help(void)
{
    uprintf("sernum,isfet=[%u chars]", MAX_ISFET_SN-1); prn_nl();
}
void apf_sernum_isfet_set(void)
{
    //0123456789012345678
    //sernum,isfet,
    rx0.buf[13+MAX_ISFET_SN-1] = 0x00;
    sprintf(sys_data.isfet_SN, "%s", &rx0.buf[13]);

    uprintf("sernum,ctd=%s", sys_data.isfet_SN); prn_nl();
    eeprom_sys_data_store();
}


//ise_SN[MAX_ISE_SN]
void apf_sernum_ise_get(void)
{
    uprintf("sernum,ise=%s", sys_data.ise_SN); prn_nl();
}

void apf_sernum_ise_help(void)
{
    uprintf("sernum,ise=[%u chars]", MAX_ISE_SN-1); prn_nl();
}
void apf_sernum_ise_set(void)
{
    //0123456789012345678
    //sernum,ise,
    rx0.buf[11+MAX_ISE_SN-1] = 0x00;
    sprintf(sys_data.ise_SN, "%s", &rx0.buf[11]);

    uprintf("sernum,ise=%s", sys_data.ise_SN); prn_nl();
    eeprom_sys_data_store();
}

//lamp_SN[MAX_LAMP_SN]
void apf_sernum_lamp_get(void)
{
    uprintf("sernum,lamp=%s", sys_data.lamp_SN); prn_nl();
}

void apf_sernum_lamp_help(void)
{
    uprintf("sernum,lamp=[%u chars]", MAX_LAMP_SN-1); prn_nl();
}
void apf_sernum_lamp_set(void)
{
    //0123456789012345678
    //sernum,lamp,
    rx0.buf[12+MAX_LAMP_SN-1] = 0x00;
    sprintf(sys_data.lamp_SN, "%s", &rx0.buf[12]);

    uprintf("sernum,lamp=%s", sys_data.lamp_SN); prn_nl();
    eeprom_sys_data_store();
}


//mcap_SN[MAX_MCAP_SN]
void apf_sernum_mcap_get(void)
{
    uprintf("sernum,mcap=%s", sys_data.mcap_SN); prn_nl();
}

void apf_sernum_mcap_help(void)
{
    uprintf("sernum,mcap=[%u chars]", MAX_MCAP_SN-1); prn_nl();
}
void apf_sernum_mcap_set(void)
{
    //0123456789012345678
    //sernum,mcap,
    rx0.buf[12+MAX_MCAP_SN-1] = 0x00;
    sprintf(sys_data.mcap_SN, "%s", &rx0.buf[12]);

    uprintf("sernum,mcap=%s", sys_data.mcap_SN); prn_nl();
    eeprom_sys_data_store();
}

//optode_SN[MAX_OPTODE_SN]
void apf_sernum_optode_get(void)
{
    uprintf("sernum,optode=%s", sys_data.optode_SN); prn_nl();
}

void apf_sernum_optode_help(void)
{
    uprintf("sernum,optode=[%u chars]", MAX_OPTODE_SN-1); prn_nl();
}
void apf_sernum_optode_set(void)
{
    //0123456789012345678
    //sernum,optode,
    rx0.buf[14+MAX_OPTODE_SN-1] = 0x00;
    sprintf(sys_data.optode_SN, "%s", &rx0.buf[14]);

    uprintf("sernum,optode=%s", sys_data.optode_SN); prn_nl();
    eeprom_sys_data_store();
}

//probe_SN[MAX_PROBE_SN]
void apf_sernum_probe_get(void)
{
    uprintf("sernum,probe=%s", sys_data.probe_SN); prn_nl();
}

void apf_sernum_probe_help(void)
{
    uprintf("sernum,probe=[%u chars]", MAX_PROBE_SN-1); prn_nl();
}
void apf_sernum_probe_set(void)
{
    //0123456789012345678
    //sernum,probe,
    rx0.buf[13+MAX_PROBE_SN-1] = 0x00;
    sprintf(sys_data.probe_SN, "%s", &rx0.buf[13]);

    uprintf("sernum,probe=%s", sys_data.probe_SN); prn_nl();
    eeprom_sys_data_store();
}



//sensor_SN[MAX_SENSOR_SN]
void apf_sernum_sensor_get(void)
{
    uprintf("sernum,phsensor=%s", sys_data.sensor_SN); prn_nl();
}

void apf_sernum_sensor_help(void)
{
    uprintf("sernum,phsensor=[%u chars]", MAX_SENSOR_SN-1); prn_nl();
}
void apf_sernum_sensor_set(void)
{
    //0123456789012345678
    //sernum,phsensor,
    rx0.buf[16+MAX_SENSOR_SN-1] = 0x00;
    sprintf(sys_data.sensor_SN, "%s", &rx0.buf[16]);

    uprintf("sernum,phsensor=%s", sys_data.sensor_SN); prn_nl();
    eeprom_sys_data_store();
}


//spec_SN[MAX_SPEC_SN]
void apf_sernum_spec_get(void)
{
    uprintf("sernum,spec=%s", sys_data.spec_SN); prn_nl();
}

void apf_sernum_spec_help(void)
{
    uprintf("sernum,spec=[%u chars]", MAX_SPEC_SN-1); prn_nl();
}
void apf_sernum_spec_set(void)
{
    //0123456789012345678
    //sernum,spec,
    rx0.buf[12+MAX_SPEC_SN-1] = 0x00;
    sprintf(sys_data.spec_SN, "%s", &rx0.buf[12]);

    uprintf("sernum,spec=%s", sys_data.spec_SN); prn_nl();
    eeprom_sys_data_store();
}



//spec_analog_SN[MAX_SPEC_ANALOG_SN]
void apf_sernum_spec_analog_get(void)
{
    uprintf("sernum,spec_analog=%s", sys_data.spec_analog_SN); prn_nl();
}

void apf_sernum_spec_analog_help(void)
{
    uprintf("sernum,spec_analog=[%u chars]", MAX_SPEC_ANALOG_SN-1); prn_nl();
}
void apf_sernum_spec_analog_set(void)
{
    //0123456789012345678
    //sernum,spec_analog,
    rx0.buf[19+MAX_SPEC_ANALOG_SN-1] = 0x00;
    sprintf(sys_data.spec_analog_SN, "%s", &rx0.buf[19]);

    uprintf("sernum,spec_analog=%s", sys_data.spec_analog_SN); prn_nl();
    eeprom_sys_data_store();
}


//spec_digital_SN[MAX_SPEC_DIGITAL_SN]
void apf_sernum_spec_digital_get(void)
{
    uprintf("sernum,spec_digital=%s", sys_data.spec_digital_SN); prn_nl();
}

void apf_sernum_spec_digital_help(void)
{
    uprintf("sernum,spec_digital=[%u chars]", MAX_SPEC_DIGITAL_SN-1); prn_nl();
}
void apf_sernum_spec_digital_set(void)
{
    //012345678901234567890
    //sernum,spec_digital,
    rx0.buf[20+MAX_SPEC_DIGITAL_SN-1] = 0x00;
    sprintf(sys_data.spec_digital_SN, "%s", &rx0.buf[20]);

    uprintf("sernum,spec_digital=%s", sys_data.spec_digital_SN); prn_nl();
    eeprom_sys_data_store();
}



//tiva_SN[MAX_TIVA_SN]
void apf_sernum_tiva_get(void)
{
    uprintf("sernum,tiva=%s", sys_data.tiva_SN); prn_nl();
}

void apf_sernum_tiva_help(void)
{
    uprintf("sernum,tiva=[%u chars]", MAX_TIVA_SN-1); prn_nl();
}
void apf_sernum_tiva_set(void)
{
    //0123456789012345678
    //sernum,tiva,
    rx0.buf[12+MAX_TIVA_SN-1] = 0x00;
    sprintf(sys_data.tiva_SN, "%s", &rx0.buf[12]);

    uprintf("sernum,tiva=%s", sys_data.tiva_SN); prn_nl();
    eeprom_sys_data_store();
}

void apf_sernum_handler(void)
{
    char    *str;
    char    *endptr;
    char    *strptr2, *strptr3, *strptr4, *strptr5;
    int     result;
    int     retStatus = 0;

    //sernum
    //sernum,?
    //sernum,h
    //sernum,1234
    //sernum,base
    //sernum,ctd
    //sernum,isfet
    //sernum,ise
    //sernum,lamp
    //sernum,mcap
    //sernum,msc   //new (was sernum)
    //sernum,optode
    //sernum,probe
    //sernum,spec
    //sernum,spec_analog
    //sernum,spec_digital
    //sernum,tiva

    if(rx0.buf[6] == 0)
    {
        apf_sernum_msc_get();
        return;
    }


    //todo, make this sernum,msc or sernum,sys
    if(rx0.buf[6] == ',' && rx0.buf[7] == '?')
    {
        apf_sernum_msc_get();
        return;
    }
    else if(rx0.buf[6] == ',' && rx0.buf[7] == 'h')
    {
        apf_sernum_help();
        return;
    }
    else if((rx0.buf[6] == ',' || rx0.buf[6] == '=') && rx0.buf[7] >= '0' && rx0.buf[7] <= '9')
    {
        apf_sernum_msc_set();
        return;
    }
    else
    {

    }

    if(strncmp((char*)&rx0.buf[7], "base", 4) == 0)      //baseboard
    {
        //0123456789012345678
        //sernum,base
        if(rx0.buf[11] == 0)
        {
            apf_sernum_baseboard_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == '?'))
        {
            apf_sernum_baseboard_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == 'h'))
        {
            apf_sernum_baseboard_help();
            return;
        }
        else
        {
            apf_sernum_baseboard_set();
        }

    }
    else if(strncmp((char*)&rx0.buf[7], "ctd", 3) == 0)
    {
        //sernum,ctd
        //0123456789012345678

        //sys_data.ctd_SN[MAX_CTD_SN];
        //uprintf("ctd"); prn_nl();
        if(rx0.buf[10] == 0)
        {
            apf_sernum_ctd_get();
            return;
        }
        else if((rx0.buf[10] == ',') &&  (rx0.buf[11] == '?'))
        {
            apf_sernum_ctd_get();
            return;
        }
        else if((rx0.buf[10] == ',') &&  (rx0.buf[11] == 'h'))
        {
            apf_sernum_ctd_help();
            return;
        }
        else
        {
            apf_sernum_ctd_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "isfet", 5) == 0)
    {
        //0123456789012345678
        //sernum,isfet

        //sys_data.isfet_SN[MAX_ISFET_SN];
        //uprintf("isfet"); prn_nl();
        if(rx0.buf[12] == 0)
        {
            apf_sernum_isfet_get();
            return;
        }
        else if((rx0.buf[12] == ',') &&  (rx0.buf[13] == '?'))
        {
            apf_sernum_isfet_get();
            return;
        }
        else if((rx0.buf[12] == ',') &&  (rx0.buf[13] == 'h'))
        {
            apf_sernum_isfet_help();
            return;
        }
        else
        {
            apf_sernum_isfet_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "ise", 3) == 0)
    {
        //0123456789012345678
        //sernum,ise

        //sys_data.ise_SN[MAX_ISE_SN];
        //uprintf("ise"); prn_nl();
        if(rx0.buf[10] == 0)
        {
            apf_sernum_ise_get();
            return;
        }
        else if((rx0.buf[10] == ',') &&  (rx0.buf[11] == '?'))
        {
            apf_sernum_ise_get();
            return;
        }
        else if((rx0.buf[10] == ',') &&  (rx0.buf[11] == 'h'))
        {
            apf_sernum_ise_help();
            return;
        }
        else
        {
            apf_sernum_ise_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "lamp", 4) == 0)
    {
        //0123456789012345678
        //sernum,lamp

        //sys_data.lamp_SN[MAX_LAMP_SN];
        //uprintf("lamp"); prn_nl();
        if(rx0.buf[11] == 0)
        {
            apf_sernum_lamp_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == '?'))
        {
            apf_sernum_lamp_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == 'h'))
        {
            apf_sernum_lamp_help();
            return;
        }
        else
        {
            apf_sernum_lamp_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "mcap", 4) == 0)
    {
        //0123456789012345678
        //sernum,mcap

        //sys_data.mcap_SN[MAX_MCAP_SN];
        //uprintf("mcap"); prn_nl();
        if(rx0.buf[11] == 0)
        {
            apf_sernum_mcap_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == '?'))
        {
            apf_sernum_mcap_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == 'h'))
        {
            apf_sernum_mcap_help();
            return;
        }
        else
        {
            apf_sernum_mcap_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "msc", 3) == 0)
    {
        //sernum,msc
        //0123456789012345678

        //sys_data.ctd_SN[MAX_MSC_SN];
        //uprintf("ctd"); prn_nl();
        if(rx0.buf[10] == 0)
        {
            apf_sernum_msc_get();
            return;
        }
        else if((rx0.buf[10] == ',') &&  (rx0.buf[11] == '?'))
        {
            apf_sernum_msc_get();
            return;
        }
        else if((rx0.buf[10] == ',') &&  (rx0.buf[11] == 'h'))
        {
            apf_sernum_msc_help();
            return;
        }
        else
        {
            apf_sernum_msc_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "optode", 6) == 0)
    {
        //0123456789012345678
        //sernum,optode

        //sys_data.optode_SN[MAX_OPTODE_SN];
        //uprintf("optode"); prn_nl();
        if(rx0.buf[13] == 0)
        {
            apf_sernum_optode_get();
            return;
        }
        else if((rx0.buf[13] == ',') &&  (rx0.buf[14] == '?'))
        {
            apf_sernum_optode_get();
            return;
        }
        else if((rx0.buf[13] == ',') &&  (rx0.buf[14] == 'h'))
        {
            apf_sernum_optode_help();
            return;
        }
        else
        {
            apf_sernum_optode_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "probe", 5) == 0)
    {
        //0123456789012345678
        //sernum,probe

        //sys_data.probe_SN[MAX_PROBE_SN];
        //uprintf("probe"); prn_nl();
        if(rx0.buf[12] == 0)
        {
            apf_sernum_probe_get();
            return;
        }
        else if((rx0.buf[12] == ',') &&  (rx0.buf[13] == '?'))
        {
            apf_sernum_probe_get();
            return;
        }
        else if((rx0.buf[12] == ',') &&  (rx0.buf[13] == 'h'))
        {
            apf_sernum_probe_help();
            return;
        }
        else
        {
            apf_sernum_probe_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "phsensor", 8) == 0)
    {
        //0123456789012345678
        //sernum,phsensor

        //sys_data.sensor_SN[MAX_SENSOR_SN];
        //uprintf("sensor"); prn_nl();
        if(rx0.buf[15] == 0)
        {
            apf_sernum_sensor_get();
            return;
        }
        else if((rx0.buf[15] == ',') &&  (rx0.buf[16] == '?'))
        {
            apf_sernum_sensor_get();
            return;
        }
        else if((rx0.buf[15] == ',') &&  (rx0.buf[16] == 'h'))
        {
            apf_sernum_sensor_help();
            return;
        }
        else
        {
            apf_sernum_sensor_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "spec_analog", 8) == 0)
    {
        //0123456789012345678
        //sernum,spec_analog

        //sys_data.spec_analog_SN[MAX_SPEC_ANALOG_SN];
        //uprintf("spec_analog"); prn_nl();
        if(rx0.buf[18] == 0)
        {
            apf_sernum_spec_analog_get();
            return;
        }
        else if((rx0.buf[18] == ',') &&  (rx0.buf[19] == '?'))
        {
            apf_sernum_spec_analog_get();
            return;
        }
        else if((rx0.buf[18] == ',') &&  (rx0.buf[19] == 'h'))
        {
            apf_sernum_spec_analog_help();
            return;
        }
        else
        {
            apf_sernum_spec_analog_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "spec_digital", 8) == 0)
    {
        //0123456789012345678
        //sernum,spec_digital

        //sys_data.spec_digital_SN[MAX_SPEC_DIGITAL_SN];
        //uprintf("spec_digital"); prn_nl();
        if(rx0.buf[19] == 0)
        {
            apf_sernum_spec_digital_get();
            return;
        }
        else if((rx0.buf[19] == ',') &&  (rx0.buf[20] == '?'))
        {
            apf_sernum_spec_digital_get();
            return;
        }
        else if((rx0.buf[19] == ',') &&  (rx0.buf[20] == 'h'))
        {
            apf_sernum_spec_digital_help();
            return;
        }
        else
        {
            apf_sernum_spec_digital_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "spec", 4) == 0)
    {
        //0123456789012345678
        //sernum,spec

        //sys_data.spec_SN[MAX_SPEC_SN];
        //uprintf("spec"); prn_nl();
        if(rx0.buf[11] == 0)
        {
            apf_sernum_spec_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == '?'))
        {
            apf_sernum_spec_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == 'h'))
        {
            apf_sernum_spec_help();
            return;
        }
        else
        {
            apf_sernum_spec_set();
        }
    }
    else if(strncmp((char*)&rx0.buf[7], "tiva", 4) == 0)
    {
        //0123456789012345678
        //sernum,tiva
        //sernum,tiva,?
        //sernum,tiva,

        //sys_data.tiva_SN[MAX_TIVA_SN];
        //uprintf("TIVA"); prn_nl();
        if(rx0.buf[11] == 0)
        {
            apf_sernum_tiva_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == '?'))
        {
            apf_sernum_tiva_get();
            return;
        }
        else if((rx0.buf[11] == ',') &&  (rx0.buf[12] == 'h'))
        {
            apf_sernum_tiva_help();
            return;
        }
        else
        {
            apf_sernum_tiva_set();
        }
    }

#ifdef NOCODE
    str = (char *)&rx0.buf[0];
    // Tokenize the string using commas as delimiters
    strtok(str, ",");  // first token CTD command
    strptr2 = strtok(NULL, ",");  // 2nd  token - first argument is type of serial number
    strptr3 = strtok(NULL, ",");  // 3rd token
    strptr4 = strtok(NULL, ",");  // 4th token
    strptr5 = strtok(NULL, ",");  // 5th token

    if ((strptr2 == 0) || (strptr3 == 0) || (strptr4 == 0) || (strptr5 == 0))
    {
        //sernum   return msc serial number
        apf_sernum_msc_get();
        return;
    }
#endif


}

void apf_status_get(void)
{

    uprintf("Watchdog    Counter,  apf_fram.sys_wdog_ctr   = %u", apf_fram.sys_wdog_ctr);  prn_nl();     //Was Power Cycle sys_por_ctr
    uprintf("System Reset Counter, apf_fram.sys_reset_ctr  = %u", apf_fram.sys_reset_ctr);   prn_nl();
    uprintf("System Error Counter, apf_fram.sys_error_ctr  = %lu", apf_fram.sys_error_ctr);   prn_nl();
    uprintf("Error Log Counter, apf_fram.errorLog_errorCnt = %lu", apf_fram.errorLog_errorCnt);   prn_nl();
    uprintf("Last System Error, apf_fram.sys_last_error    = %d",  apf_fram.sys_last_error); prn_nl();  // JLD - need to show as signed values
    uprintf("LogFile Counter, apf_fram.profileCnt          = %u",  apf_fram.profileCnt); prn_nl();
    prn_nl();
    //uprintf("File sizes = ",  ); prn_nl();
    //uprintf("Depoy Mode = ???"); prn_nl();
    uprintf("Sensor Mode = %u", apf_fram.sys_sensor_mode); prn_nl();
    //uprintf("Data Mode   = %u", apf_fram.sys_data_mode); prn_nl();
    uprintf("Power Active Timeout Period = %u", apf_eeprom.sys_apf_tmout); prn_nl();
    uprintf("Command Time Terminate = %u msec (%u sec)", apf_h2m_timeout_get(), apf_h2m_timeout_get()/1000); prn_nl();
    prn_nl();
    wait_consoleTx();
    uprintf("gotSample = %u", apf.gotSample); prn_nl();
    uprintf("apf.acquire_time           = %.3f", apf.acquire_time); prn_nl();
    uprintf("apf_fram.isus_sample_ctr   = %u", apf_fram.isus_sample_ctr); prn_nl();
    uprintf("apf_fram.isus_sample_time  = %u", apf_fram.isus_sample_time); prn_nl();
    uprintf("apf_fram.isus_sample_time  = %s",  DisplayFormattedTime(apf_fram.isus_sample_time)); prn_nl();
    uprintf("apf_fram.dura_sample_ctr   = %u", apf_fram.dura_sample_ctr); prn_nl();
    uprintf("apf_fram.dura_sample_time  = %u", apf_fram.dura_sample_time); prn_nl();
    uprintf("apf_fram.dura_sample_time  = %s", DisplayFormattedTime(apf_fram.dura_sample_time)); prn_nl();
    prn_nl();
    uprintf("apf_fram.errorLog_errorCnt = %u", apf_fram.errorLog_errorCnt); prn_nl();
    uprintf("apf_fram.sys_error_ctr     = %u", apf_fram.sys_error_ctr); prn_nl();
    uprintf("apf_fram.sys_last_error    = %d", apf_fram.sys_last_error); prn_nl();       // JLD - need to show as signed values
    uprintf("apf_fram.sys_error_time    = %u", apf_fram.sys_error_time); prn_nl();
    uprintf("apf_fram.sys_error_time    = %s", DisplayFormattedTime(apf_fram.sys_error_time)); prn_nl();
    wait_consoleTx();
    uprintf("apf_fram.dura_error_ctr    = %u", apf_fram.dura_error_ctr); prn_nl();
    uprintf("apf_fram.dura_last_error   = %d", apf_fram.dura_last_error); prn_nl();     // JLD - need to show as signed values
    uprintf("apf_fram.dura_error_time   = %u", apf_fram.dura_error_time); prn_nl();
    uprintf("apf_fram.dura_error_time   = %s", DisplayFormattedTime(apf_fram.dura_error_time)); prn_nl();
    uprintf("apf_fram.dura_reset_flag   = %u", apf_fram.dura_reset_flag); prn_nl();
    prn_nl();
    wait_consoleTx();
    uprintf("apf_fram.sendlast_byteCnt  = %u", apf_fram.sendlast_byteCnt); prn_nl();
    uprintf("apf_fram.isus_error_ctr    = %u", apf_fram.isus_error_ctr); prn_nl();
    uprintf("apf_fram.isus_last_error   = %d", apf_fram.isus_last_error); prn_nl();     // JLD - need to show as signed values
    uprintf("apf_fram.isus_error_time   = %u", apf_fram.isus_error_time); prn_nl();
    uprintf("apf_fram.isus_error_time   = %s", DisplayFormattedTime(apf_fram.isus_error_time)); prn_nl();

    uprintf("apf_fram.isus_reset_flag   = %u", apf_fram.isus_reset_flag); prn_nl();
    uprintf("apf_fram.isus_lampOn_mSec  = %u", apf_fram.isus_lampOn_mSec); prn_nl();
    uprintf("apf_fram.isus_lamp_evt     = %u", apf_fram.isus_lampOn_ctr); prn_nl();
    prn_nl();
    wait_consoleTx();

    uprintf("apf_fram.logIsus_fsize     = %u", apf_fram.logIsus_fsize); prn_nl();
    uprintf("apf_fram.logDura_fsize     = %u", apf_fram.logIsus_fsize); prn_nl();

    uprintf("sizeof(sys_data) = %u, sizeof(sys_qa) = %u, sizeof(apf) = %u, sizeof(apf_fram) = %u, sizeof(apf_eeprom) = %u", sizeof(sys_data), sizeof(sys_qa), sizeof(apf), sizeof(apf_fram), sizeof(apf_eeprom) );

//Power Up Time   (GMT) , %02d/%02d/%02d %02d:%02d:%02d", tp->tm_mon+1, tp->tm_mday, tp->tm_year+1900, tp->tm_hour, tp->tm_min, tp->tm_sec);

}

void apf_status_help(void)
{
    uprintf("MSC1 Logger Setup menu items are available as status"); prn_nl();
    uprintf("status        - print status"); prn_nl();
    uprintf("status,?      - print status"); prn_nl();
    uprintf("status,reset  - reset status variables"); prn_nl();
}

void apf_status_reset(void)
{
    uprintf("apf_status_reset() NEEDS DATA INTEGRITY CHECK, por favor"); prn_nl();

    apf_fram.sys_por_flag = FALSE;

    apf_fram.profileCnt = 0;
    apf_fram.sys_sensor_mode = ISUS_SENSOR_MODE;     //default to MODE 1
    apf_fram.sys_data_mode = SYS_DATAMODE_VAL;
    apf_eeprom.sys_apf_tmout = 10;         //APFTODO, make a #define, see apf_init.c

    apf_fram.sys_last_error = 0;

    apf_fram.sys_error_ctr = 0;
    apf_fram.sys_error_time = 0;
    apf_fram.sys_reset_ctr = 0;
    apf_fram.sys_reset_time = SYS_RESETTIME_VAL;
    apf_fram.sys_wdog_ctr = 0;                           //wdog counter
    apf_fram.sys_file_ctr = SYS_FILECTR_VAL;
    apf_eeprom.sys_sleep_period = SYS_SLEEPPER_VAL;
    apf_eeprom.sys_pump_period = SYS_PUMPPER_VAL;

    apf_fram.dura_error_time = DURA_LASTERRTIME_VAL;
    apf_fram.dura_sample_time = DURA_LASTSAMPTIME_VAL;
    apf_fram.dura_error_ctr = DURA_ERRORCTR_VAL;
    apf_fram.dura_last_error = DURA_LASTERROR_VAL;
    apf_fram.dura_reset_flag = TRUE;                     //shouldnt this be false?  APFTODO
    apf_fram.dura_sample_ctr = DURA_SAMPLECTR_VAL;

    if(fram_apf_vars_store() <= 0)
    {
        LogErrorMsg("Failed to write to fram in apf_status_reset", (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
    }
    if(eeprom_apf_cfg_store() != 0)
    {
        LogErrorMsg("Failed to write to eeprom in apf_status_reset", (int)EEPROM_FAIL, apf_eeprom.sys_message_enable);
    }

}


void apf_vinmin_get(void)
{
    //uprintf("Vin min threshold for ISUS nitrate = %.1f", sys_data.isus_vin_min_volt); prn_nl();

    //uprintf("Vin min threshold for DURA pH      = %.1f", sys_data.dura_vin_min_volt); prn_nl();       // was low_batt_volt

    uprintf("vinmin,isus=%.1f", sys_data.isus_vin_min_volt); prn_nl();
    uprintf("vinmin,dura=%.1f", sys_data.dura_vin_min_volt); prn_nl();
}

void apf_vinmin_isus_get(void)
{
    uprintf("vinmin,isus=%.1f", sys_data.isus_vin_min_volt); prn_nl();
}

void apf_vinmin_dura_get(void)
{
    uprintf("vinmin,dura=%.1f", sys_data.dura_vin_min_volt); prn_nl();
}

void apf_vinmin_help(void)
{
    //       012345678901234
    uprintf("vinmin,dura,8.0      - Vin min threshold for DURA pH"); prn_nl();
    uprintf("vinmin,isus,11.2     - Vin min threshold for ISUS nitrate"); prn_nl();
    prn_nl();
}

void apf_vinmin_isus_set(void)
{
    char    *endptr;
    char    *str;
    float   fVinmin;

    str = (char *)&rx0.buf[12];

    errno = 0; // Reset errno
    fVinmin = strtof(str, &endptr);

    if( (fVinmin >= 6.0) && (fVinmin <= 22.0) )
    {
        sys_data.isus_vin_min_volt = fVinmin;
        eeprom_sys_data_store();
        uprintf("vinmin,isus,%.2f stored in eeprom", sys_data.isus_vin_min_volt); prn_nl();
    }
    else
    {
        uprintf("vinmin,isus,%.2f range error [6.0 : 22.0]", sys_data.isus_vin_min_volt); prn_nl();
    }
}

void apf_vinmin_dura_set(void)
{
    char    *endptr;
    char    *str;
    float   fVinmin;

    str = (char *)&rx0.buf[12];

    errno = 0; // Reset errno
    fVinmin = strtof(str, &endptr);

    if( (fVinmin >= 6.0) && (fVinmin <= 22.0) )
    {
        sys_data.dura_vin_min_volt = fVinmin;
        eeprom_sys_data_store();
        uprintf("vinmin,dura,%.2f stored in eeprom", sys_data.dura_vin_min_volt); prn_nl();
    }
    else
    {
        uprintf("vinmin,dura,%.2f range error [6.0 : 22.0]", sys_data.dura_vin_min_volt); prn_nl();
    }

}

void cmd_wisdom_get(void)
{
    uint32_t indx, indx2;

    srand((unsigned) read_hib_rtcc());

    // generate number 0 to 61
    indx = rand() % 62;  // todo, how to get the number of strings in wisdom_arr
    indx2 = strlen((char *)wisdom_arr[indx]);
    indx2 /= 15;

    uprintf("  %s", wisdom_arr[indx]); prn_nl();

    wait_consoleTx();

}

void cmd_yooster(void)
{
    uint32_t indx;

    //uprintf("yooster Imp Es Nada"); prn_nl();
    indx=0;
    for(indx=0; indx<100; indx++)
    {
        if(contrib_arr[indx] == 0)
        {
            prn_nl();
            break;
        }
        uprintf("  %s", contrib_arr[indx]); prn_nl();
        delay_msec(200);
    }
    //List contributors

}

void apf_error_log_add(unsigned char *errStr)
{
    char errBuf[256];

    sprintf(errBuf, "%s", errStr);
    LogErrorMsg(errBuf, (int)ERROR_LOG_TEST, apf_eeprom.sys_message_enable);
}

void apf_error_log_dura_add(unsigned char *errStr)
{
    char errBuf[256];

    sprintf(errBuf, "%s", errStr);
    LogErrorMsg(errBuf, (int)ERROR_LOG_DURA_TEST, apf_eeprom.sys_message_enable);
}

void apf_error_log_isus_add(unsigned char *errStr)
{
    char errBuf[256];

    sprintf(errBuf, "%s", errStr);
    LogErrorMsg(errBuf, (int)ERROR_LOG_ISUS_TEST, apf_eeprom.sys_message_enable);
}

void apf_test_wdog(void)
{

    uprintf("test,wdog:  enable watchdog by removing jumper, code will loop for 2 seconds without wdog_pet");  prn_nl();
    while(1)
        ;

}

void cmd_k0_help(void)
{
    uprintf("k0_hcl=%f", sys_data.k0_HCL); prn_nl();
    uprintf("k0_sw=%f", sys_data.k0_SW); prn_nl();
    uprintf("k0std_sw=%f", sys_data.k0std_SW); prn_nl();

    uprintf("k2[0]=%.5e", sys_data.K2_Cal[0]); prn_nl();
    uprintf("k2[1]=%.5e", sys_data.K2_Cal[1]); prn_nl();
    uprintf("k2[2]=%.5e", sys_data.K2_Cal[2]); prn_nl();
    uprintf("k2[3]=%.5e", sys_data.K2_Cal[3]); prn_nl();

    uprintf("fp[0]=%.5e", sys_data.fP_Cal[0]); prn_nl();
    uprintf("fp[1]=%.5e", sys_data.fP_Cal[1]); prn_nl();
    uprintf("fp[2]=%.5e", sys_data.fP_Cal[2]); prn_nl();
    uprintf("fp[3]=%.5e", sys_data.fP_Cal[3]); prn_nl();
    uprintf("fp[4]=%.5e", sys_data.fP_Cal[4]); prn_nl();
    uprintf("fp[5]=%.5e", sys_data.fP_Cal[5]); prn_nl();
    uprintf("fp[6]=%.5e", sys_data.fP_Cal[6]); prn_nl();

}

// input string pointing to float
float cmd_k0_hcl(uint8_t *fStr)
{
    float  fVal;
    char   *strptr;
    char   *endptr;

    strptr = (char *)fStr;

    //sys_data.k0_HCL;
    //k0_hcl=12345.678
    //01234567
    errno = 0;
    fVal = strtof(strptr, &endptr);
    if( errno == ERANGE )
    {
        uprintf("K0_HCL OverflowErr: %s", strptr); prn_nl();
        return(0.0);
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("K0_HCL ConvertErr: %s", strptr);  prn_nl();
        return(0.0);
    }
    else
    {

    }

    sys_data.k0_HCL = fVal;
    eeprom_sys_data_store();

    uprintf("k0_hcl=%f", sys_data.k0_HCL); prn_nl();

    return(sys_data.k0_HCL);
}

float cmd_k0_sw(uint8_t *fStr)
{
    float  fVal;
    char   *strptr;
    char   *endptr;

    strptr = (char *)fStr;
    //sys_data.k0_SW;
    //k0_sw,12345.678
    //01234567
    errno = 0;
    fVal = strtof(strptr, &endptr);
    if( errno == ERANGE )
    {
        uprintf("k0_SW OverflowErr: %s", strptr); prn_nl();
        return(0.0);
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("k0_SW ConvertErr: %s", strptr);  prn_nl();
        return(0.0);
    }
    else
    {

    }

    sys_data.k0_SW = fVal;
    eeprom_sys_data_store();

    uprintf("k0_sw=%f", sys_data.k0_SW); prn_nl();

    return(sys_data.k0_SW);

}

float cmd_k0std_sw(uint8_t *fStr)
{
    float  fVal;
    char   *strptr;
    char   *endptr;

    strptr = (char *)fStr;
    //sys_data.k0std_SW;
    //k0std_sw,12345.678
    //0123456789
    errno = 0;
    fVal = strtof(strptr, &endptr);
    if( errno == ERANGE )
    {
        uprintf("k0std_SW OverflowErr: %s", strptr); prn_nl();
        return(0.0);
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("k0std_SW ConvertErr: %s", strptr);  prn_nl();
        return(0.0);
    }
    else
    {

    }

    sys_data.k0std_SW = fVal;
    eeprom_sys_data_store();

    uprintf("k0std_sw=%f", sys_data.k0std_SW); prn_nl();

    return(sys_data.k0std_SW);

}

float cmd_k2(uint8_t *fStr)
{
    uint32_t indx;
    float  fVal;
    char   *strptr;
    char   *endptr;

    strptr = (char *)fStr;

    //sys_data.K2_Cal[4];
    //k2[3]=12345.678
    //0123456789
    indx = 0;
    indx = *strptr & 0x0f;
    //uprintf("DBG indx = %u", indx); prn_nl();
    if(indx >= MAX_NUMOF_K2_CAL_FIELDS)
    {
        uprintf("k2[%u], array index out of range [0:%u]", indx, (MAX_NUMOF_K2_CAL_FIELDS-1)); prn_nl();
        return(0.0);
    }

    //check for end of string, that is a 'get'
    strptr += 2;
    if(*strptr == 0)
    {
        uprintf("k2[%u]=%.5e", indx, sys_data.K2_Cal[indx]); prn_nl();
        return(sys_data.K2_Cal[indx]);
    }

    // process as a 'set' if = or , or ' '
    strptr++;
    errno = 0;
    fVal = strtof(strptr, &endptr);
    if( errno == ERANGE )
    {
        uprintf("K2_Cal[%u] OverflowErr: %s", indx, strptr); prn_nl();
        return(0.0);
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("K2_Cal[%u] ConvertErr: %s", indx, strptr);  prn_nl();
        return(0.0);
    }
    else
    {

    }

    sys_data.K2_Cal[indx] = fVal;
    eeprom_sys_data_store();

    uprintf("k2[%u]=%.5e", indx, sys_data.K2_Cal[indx]); prn_nl();

    return(sys_data.K2_Cal[indx]);
}

//"fp[
float cmd_fp(uint8_t *fStr)
{
    uint32_t indx;
    float  fVal;
    char   *strptr;
    char   *endptr;

    strptr = (char *)fStr;

    //sys_data.fP_Cal[7];
    //fp[6]=12345.678
    //0123456789
    indx = 0;
    indx = *strptr & 0x0f;
    //uprintf("DBG indx = %u", indx); prn_nl();
    if(indx >= MAX_NUMOF_FP_CAL_FIELDS)
    {
        uprintf("fp[%u], array index out of range [0:%u]", indx, (MAX_NUMOF_FP_CAL_FIELDS-1)); prn_nl();
        return(0.0);
    }

    //check for end of string, that is a 'get'
    strptr += 2;
    if(*strptr == 0)
    {
        uprintf("fp[%u]=%.5e", indx, sys_data.fP_Cal[indx]); prn_nl();
        return(sys_data.fP_Cal[indx]);
    }

    // process as a 'set' if = or , or ' '
    strptr++;
    errno = 0;
    fVal = strtof(strptr, &endptr);
    if( errno == ERANGE )
    {
        uprintf("fp[%u] OverflowErr: %s", indx, strptr); prn_nl();
        return(0.0);
    }
    else if ((errno == EINVAL) || (*endptr))
    {
        uprintf("fp[%u] ConvertErr: %s", indx, strptr);  prn_nl();
        return(0.0);
    }
    else
    {

    }

    sys_data.fP_Cal[indx] = fVal;
    eeprom_sys_data_store();

    uprintf("fp[%u]=%.5e", indx, sys_data.fP_Cal[indx]); prn_nl();

    return(sys_data.fP_Cal[indx]);
}



void cmd_cal_date_isus(unsigned char *dateStr)
{
    //cal_date,isus=04/27/2024
    //0123456789012345678901
    int size;

    size = strlen((const char *)dateStr);
    if(size < MAX_ISUS_CAL_DATE)
    {
        fram_apfArray_refresh();

        sprintf(apfArray.CalDate, "%s", dateStr);
        uprintf("cal_date,isus=%s", apfArray.CalDate); prn_nl();
        fram_apfArray_store();
    }
    else
    {
        uprintf("cal_date,isus string too long, max %u.  %s", MAX_ISUS_CAL_DATE-1, dateStr); prn_nl();
    }

}

void cmd_cal_date_pt(unsigned char *dateStr)
{
    //cal_date,pt=04/27/2024
    //0123456789012345678901
    int size;

    size = strlen((const char *)dateStr);
    if(size < MAX_PT_CAL_DATE)
    {
        sprintf(sys_data.PT_Cal_date, "%s", dateStr);
        uprintf("cal_date,pt=%s", sys_data.PT_Cal_date); prn_nl();
        eeprom_sys_data_store();
    }
    else
    {
        uprintf("cal_date,pt string too long, max %u.  %s", MAX_PT_CAL_DATE-1, dateStr); prn_nl();
    }

}


void cmd_cal_date_k0(unsigned char *dateStr)
{
    //cal_date,k0=04/27/2024
    //0123456789012345678901
    int size;

    size = strlen((const char *)dateStr);
    if(size < MAX_K0_CAL_DATE)
    {
        sprintf(sys_data.k0_Cal_date, "%s", dateStr);
        //uprintf("k0_Cal_date=%s", sys_data.k0_Cal_date); prn_nl();
        uprintf("cal_date,k0=%s", sys_data.k0_Cal_date); prn_nl();
        eeprom_sys_data_store();
    }
    else
    {
        uprintf("cal_date,k0 string too long, max %u.  %s", MAX_K0_CAL_DATE-1, dateStr); prn_nl();
    }

}

void cmd_dura_cal_file(unsigned char *filenameStr)
{
    int size;

    size = strlen((const char *)filenameStr);
    if(size < MAX_CAL_FILENAME)
    {
        sprintf(sys_data.cal_filename, "%s", filenameStr);
        //uprintf("DuraCalFile=%s", sys_data.cal_filename); prn_nl();
        uprintf("pH_Calfile=%s", sys_data.cal_filename); prn_nl();
        eeprom_sys_data_store();
    }
    else
    {
        uprintf("DuraCalFile string too long, max %u.  %s", MAX_CAL_FILENAME-1, filenameStr); prn_nl();
    }
}


void cmd_isfet_sn(unsigned char *snStr)
{
    int size;

    size = strlen((const char *)snStr);
    if(size < MAX_ISFET_SN)
    {
        sprintf(sys_data.isfet_SN, "%s", snStr);
        uprintf("isfet_SN=%s", sys_data.isfet_SN); prn_nl();
        eeprom_sys_data_store();
    }
    else
    {
        uprintf("isfet_SN string too long, max %u.  %s", MAX_ISFET_SN-1, snStr); prn_nl();
    }
}

void cmd_ise_sn(unsigned char *snStr)
{
    int size;

    size = strlen((const char *)snStr);
    if(size < MAX_ISE_SN)
    {
        sprintf(sys_data.ise_SN, "%s", snStr);
        uprintf("ise_SN=%s", sys_data.ise_SN); prn_nl();
        eeprom_sys_data_store();
    }
    else
    {
        uprintf("ise_SN string too long, max %u.  %s", MAX_ISE_SN-1, snStr); prn_nl();
    }
}

void cmd_sensor_sn(unsigned char *snStr)
{
    int size;

    size = strlen((const char *)snStr);
    if(size < MAX_SENSOR_SN)
    {
        sprintf(sys_data.sensor_SN, "%s", snStr);
        //uprintf("sensor_SN=%s", sys_data.sensor_SN); prn_nl();
        uprintf("sernum,phsensor=%s", sys_data.sensor_SN); prn_nl();
        eeprom_sys_data_store();
    }
    else
    {
        uprintf("sensor_SN string too long, max %u.  %s", MAX_SENSOR_SN-1, snStr); prn_nl();
    }
}



void apf_print_termtime(void)
{
    uprintf("term time = %u", rx0.tick_ms_delim); prn_nl();
    //apf_m2m_mode_set(0);
}
