Beispiel #1
0
void getParamLibString(uchar index, uchar *data){
	switch(index){
	case Param_battery_type:{
		getBatteryType(data);
	}break;
	case Param_en_charge:{
		sprintf(data,"En_Charge:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_en_charge]);
	}break;
	case Param_eq_charge:{
		sprintf(data,"Eq_Charge:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_eq_charge]);
	}break;
	case Param_flo_charge:{
		sprintf(data,"Fl_Charge:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_flo_charge]);
	}break;
	case Param_time_change:{
		sprintf(data,"TIME:     %03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_time_change]);
	}break;
	case Param_under_vol:{
		sprintf(data,"UNDER VOL:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_under_vol]);
	}break;
	case Param_re_under:{
		sprintf(data,"RE UNDER :%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_re_under]);
	}break;
	case Param_over_vol:{
		sprintf(data,"VOER  VOL:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_over_vol]);
	}break;
	case Param_re_over:{
		sprintf(data,"RE   OVER:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_re_over]);
	}break;
	case Param_day_vol:{
		sprintf(data,"DAY   VOL:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_day_vol]);
	}break;
	case Param_night_vol:{
		sprintf(data,"NIGHT VOL:%03.01fV", ParamConfig[HW_BATTERY_TYPE][Param_night_vol]);
	}break;
	}
}
static void parseCommand(uint8_t* line)
{
/* ======================== Action commands ========================  */
/**
<b>Action Commands</b> */
    if (line[0] == 'a')
    {
/**
<ul>
<li> <b>Snm</b> Manually set Switch. Follow by battery n (1-3, 0 = none) and
load m (0-1)/panel (2). Each two-bit field represents a load or panel, and the
setting is the battery to be connected (no two batteries can be connected to a
load/panel). */
        switch (line[1])
        {
        case 'S':
            {
                uint8_t battery = line[2]-'0';
                uint8_t setting = line[3]-'0'-1;
                if ((battery < 4) && (setting < 4)) setSwitch(battery, setting);
                if (setting == 2) setPanelSwitchSetting(battery);
                break;
            }
/**
<li> <b>Rn</b> Reset a tripped overcurrent circuit breaker.
Set a FreeRTOS timer to expire after 250ms at which time the reset line is
released. The command is followed by an interface number n=0-5 being batteries
1-3, loads 1-2 and module. */
        case 'R':
            {
                portTickType resetTime = 250;
                intf = line[2]-'0';
                if (intf > NUM_IFS-1) break;
                xTimerHandle resetHandle
                    = xTimerCreate("Reset",resetTime,pdFALSE,(void *)intf,resetCallback);
                if(resetHandle == NULL) break;
                if (xTimerStart(resetHandle,0) != pdPASS) break;
                overCurrentReset(intf);
                break;
            }
/**
<li> <b>W</b> Write the current configuration block to FLASH */
        case 'W':
            {
                writeConfigBlock();
                break;
            }
/**
<li> <b>E</b> Send an ident response */
        case 'E':
            {
                char ident[35] = "Battery Management System,";
                stringAppend(ident,FIRMWARE_VERSION);
                stringAppend(ident,",");
                char version[3];
                intToAscii(VERSION,version);
                stringAppend(ident,version);
                sendStringLowPriority("dE",ident);
                break;
            }
/**
<li> <b>B</b> Set the battery SoC from the measured OCV */
        case 'B':
            {
                uint8_t battery = line[2]-'1';
                setBatterySoC(battery,computeSoC(getBatteryVoltage(battery),
                           getTemperature(),getBatteryType(battery)));
                break;
            }
        }
    }
/**
</ul> */
/* ======================== Data request commands ================  */
/**
<b>Data Request Commands</b> */
    else if (line[0] == 'd')
    {
        switch (line[1])
        {
/**
<ul>
<li> <b>S</b> Ask for all switch settings to be sent as well as control
settings. */
        case 'S':
            {
                sendResponse("dS",(int)getSwitchControlBits());
                uint8_t controlByte = getControls();
                sendResponse("dD",controlByte);
                break;
            }
/**
<li> <b>Bn</b> Ask for battery n=1-3 parameters to be sent */
        case 'B':
            {
                char id[] = "pR0";
                id[2] = line[2];
                uint8_t battery = line[2] - '1';
                dataMessageSend(id,getBatteryResistanceAv(battery),0);
                id[1] = 'T';
                dataMessageSend(id,(int32_t)configData.config.batteryType[battery],
                                   (int32_t)configData.config.batteryCapacity[battery]);
                id[1] = 'F';
                dataMessageSend(id,(int32_t)configData.config.
                                            floatStageCurrentScale[battery],
                                   (int32_t)configData.config.floatVoltage[battery]);
                id[1] = 'A';
                dataMessageSend(id,(int32_t)configData.config.
                                            bulkCurrentLimitScale[battery],
                                   (int32_t)configData.config.absorptionVoltage[battery]);
                break;
            }
/**
<li> <b>T</b> Ask for monitor strategy parameters to be sent. */
        case 'T':
            {
                char id[] = "pts";
                dataMessageSend(id,(int32_t)configData.config.monitorStrategy,0);
                id[2] = 'V';
                dataMessageSend(id,(int32_t)configData.config.lowVoltage,
                                   (int32_t)configData.config.criticalVoltage);
                id[2] = 'S';
                dataMessageSend(id,(int32_t)configData.config.lowSoC,
                                   (int32_t)configData.config.criticalSoC);
                id[2] = 'F';
                dataMessageSend(id,(int32_t)configData.config.floatBulkSoC,0);
                break;
            }
/**
<li> <b>C</b> Ask for charger strategy parameters to be sent. */
        case 'C':
            {
                char id[] = "pcs";
                dataMessageSend(id,(int32_t)configData.config.chargerStrategy,0);
                id[2] = 'R';
                dataMessageSend(id,(int32_t)configData.config.restTime,
                                   (int32_t)configData.config.absorptionTime);
                id[2] = 'D';
                dataMessageSend(id,(int32_t)configData.config.minDutyCycle,0);
                id[2] = 'F';
                dataMessageSend(id,(int32_t)configData.config.floatTime,
                                   (int32_t)configData.config.floatBulkSoC);
                break;
            }
        }
    }
/**
</ul> */
/* ================ Parameter Setting commands ================  */
/**
<b>Parameter Setting Commands</b> */
    else if (line[0] == 'p')
    {
        uint8_t battery = line[2]-'1';
        switch (line[1])
        {
/**
<ul>
<li> <b>a-, a+</b> Turn autoTracking on or off */
        case 'a':
            {
                if (line[2] == '-') configData.config.autoTrack = false;
                else if (line[2] == '+') configData.config.autoTrack = true;
                break;
            }
/**
<li> <b>c-, c+</b> Turn communications sending on or off */
        case 'c':
            {
                if (line[2] == '-') configData.config.enableSend = false;
                else if (line[2] == '+') configData.config.enableSend = true;
                break;
            }
/**
<li> <b>C</b> Start a calibration sequence */
        case 'C':
            {
                startCalibration();
                break;
            }
/**
<li> <b>d-, d+</b> Turn on debug messages */
        case 'd':
            {
                if (line[2] == '+') configData.config.debugMessageSend = true;
                if (line[2] == '-') configData.config.debugMessageSend = false;
                break;
            }
/**
<li> <b>Hxxxx</b> Set time from an ISO 8601 formatted string. */
        case 'H':
            {
                setTimeFromString((char*)line+2);
                break;
            }
/**
<li> <b>M-, M+</b> Turn on/off data messaging (mainly for debug) */
        case 'M':
            {
                if (line[2] == '-') configData.config.measurementSend = false;
                else if (line[2] == '+') configData.config.measurementSend = true;
                break;
            }
/**
<li> <b>r-, r+</b> Turn recording on or off */
        case 'r':
            {
                if (line[2] == '-') configData.config.recording = false;
                else if ((line[2] == '+') && (writeFileHandle < 0x0FF))
                    configData.config.recording = true;
                break;
            }
/*--------------------*/
/* BATTERY parameters */
/**
<li> <b>Tntxx</b> Set battery type and capacity, n is battery, t is type,
xx is capacity */
        case 'T':
            {
                if (battery < 3)
                {
                    uint8_t type = line[3]-'0';
                    if (type < 3)
                    {
                        configData.config.batteryType[battery] =
                            (battery_Type)type;
                        configData.config.batteryCapacity[battery] =
                            asciiToInt((char*)line+4);
                        setBatteryChargeParameters(battery);
                    }
                }
                break;
            }
/**
<li> <b>m-, m+</b> Turn on/off battery missing */
        case 'm':
            {
                if (line[3] == '-') setBatteryMissing(battery,false);
                else if (line[3] == '+') setBatteryMissing(battery,true);
                break;
            }
/**
<li> <b>Inxx</b> Set bulk current limit, n is battery, xx is limit */
        case 'I':
            {
                if (battery < 3)
                    configData.config.bulkCurrentLimitScale[battery] =
                        asciiToInt((char*)line+3);
                break;
            }
/**
<li> <b>Anxx</b> Set battery gassing voltage limit, n is battery, xx is limit */
        case 'A':
            {
                if (battery < 3)
                    configData.config.absorptionVoltage[battery] =
                        asciiToInt((char*)line+3);
                break;
            }
/**
<li> <b>fnxx</b> Set battery float current trigger, n is battery, xx is trigger */
        case 'f':
            {
                if (battery < 3)
                    configData.config.floatStageCurrentScale[battery] =
                        asciiToInt((char*)line+3);
                break;
            }
/**
<li> <b>Fnxx</b> Set battery float voltage limit, n is battery, xx is limit */
        case 'F':
            {
                if (battery < 3)
                    configData.config.floatVoltage[battery] =
                        asciiToInt((char*)line+3);
                break;
            }
/**
<li> <b>zn</b> zero current calibration by forcing current offset, n is battery */
        case 'z':
            {
                if (battery < 3)
                    setCurrentOffset(battery,getCurrent(battery));
                break;
            }
/*--------------------*/
/* MONITOR parameters */
/**
<li> <b>sm</b> Set monitor strategy byte m for keeping isolation or avoiding
loading the battery under charge. */
        case 's':
            {
                uint8_t monitorStrategy = line[2]-'0';
                if (monitorStrategy <= 3)
                    configData.config.monitorStrategy = monitorStrategy;
                break;
            }
/**
<li> <b>vx</b> set low voltage threshold, x is voltage times 256. */
        case 'v':
            {
                configData.config.lowVoltage = asciiToInt((char*)line+2);
                break;
            }
/**
<li> <b>Vx</b> set critical voltage threshold, x is voltage times 256. */
        case 'V':
            {
                configData.config.criticalVoltage = asciiToInt((char*)line+2);
                break;
            }
/**
<li> <b>xx</b> set low SoC threshold, x is voltage times 256. */
        case 'x':
            {
                configData.config.lowSoC = asciiToInt((char*)line+2);
                break;
            }
/**
<li> <b>Xx</b> set critical SoC threshold, x is voltage times 256. */
        case 'X':
            {
                configData.config.criticalSoC = asciiToInt((char*)line+2);
                break;
            }
/*--------------------*/
/* CHARGER parameters */
/**
<li> <b>Sm</b> set charger strategy byte m. */
        case 'S':
            {
                uint8_t chargerStrategy = line[2]-'0';
                if (chargerStrategy < 2)
                    configData.config.chargerStrategy = chargerStrategy;
                break;
            }
/**
<li> <b>Rx</b> set charger algorithm minimum rest time x in seconds. */
        case 'R':
            {
                configData.config.restTime = asciiToInt((char*)line+2);
                break;
            }
/**
<li> <b>Ax</b> set charger algorithm minimum gassing phase time x in seconds. */
        case 'G':
            {
                configData.config.absorptionTime = asciiToInt((char*)line+2);
                break;
            }
/**
<li> <b>Dx</b> set charger minimum duty cycle x in seconds. */
        case 'D':
            {
                configData.config.minDutyCycle = asciiToInt((char*)line+2);
                break;
            }
/**
<li> <b>Fx</b> set charger time to float x in seconds. */
        case 'e':
            {
                configData.config.floatTime = asciiToInt((char*)line+2);
                break;
            }
/**
<li> <b>Bx</b> set charger SoC x to change from float to bulk phase. */
        case 'B':
            {
                configData.config.floatBulkSoC = asciiToInt((char*)line+2);
                break;
            }
        }
    }
/**
</ul> */

/* ======================== File commands ================ */
/*
F           - get free clusters
Wfilename   - Open file for read/write. Filename is 8.3 string style. Returns handle.
Rfilename   - Open file read only. Filename is 8.3 string style. Returns handle.
Xfilename   - Delete the file. Filename is 8.3 string style.
Cxx         - Close file. x is the file handle.
Gxx         - Read a record from read or write file.
Ddirname    - Get a directory listing. Directory name is 8.3 string style.
d[dirname]  - Get the first (if dirname present) or next entry in directory.
s           - Get status of open files and configData.config.recording flag
M           - Mount the SD card.
All commands return an error status byte at the end.
Only one file for writing and a second for reading is possible.
Data is not written to the file externally. */
/**
<b>File Commands</b> */
    else if (line[0] == 'f')
    {
        switch (line[1])
        {
/**
<ul>
<li> <b>F</b> Return number of free clusters followed by the cluster size in bytes. */
            case 'F':
            {
                uint8_t wordBuf;
                uint32_t freeClusters = 0;
                uint32_t sectorCluster = 0;
                uint8_t fileStatus = FR_INT_ERR;
                if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                {
                    sendFileCommand('F',0,line+2);
                    uint8_t i;
                    for (i=0; i<4; i++)
                    {
                        wordBuf = 0;
                        xQueueReceive(fileReceiveQueue,&wordBuf,portMAX_DELAY);
                        freeClusters |= (wordBuf << 8*i);
                    }
                    for (i=0; i<4; i++)
                    {
                        wordBuf = 0;
                        xQueueReceive(fileReceiveQueue,&wordBuf,portMAX_DELAY);
                        sectorCluster |= (wordBuf << 8*i);
                    }
                    dataMessageSend("fF",freeClusters,sectorCluster);
                    xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                    xSemaphoreGive(fileSendSemaphore);
                }
                sendResponse("fE",(uint8_t)fileStatus);
                break;
            }
/**
<li> <b>Wf</b> Open a file f=filename for writing */
            case 'W':
            {
                if (stringLength((char*)line+2) < 12)
                {
                    uint8_t fileStatus = FR_INT_ERR;
                    if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                    {
                        stringCopy(writeFileName,(char*)line+2);
                        sendFileCommand('W',13,line+2);
                        xQueueReceive(fileReceiveQueue,&writeFileHandle,portMAX_DELAY);
                        sendResponse("fW",writeFileHandle);
                        xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                        xSemaphoreGive(fileSendSemaphore);
                    }
                    sendResponse("fE",(uint8_t)fileStatus);
                }
                break;
            }
/**
<li> <b>Rf</b> Open a file f=filename for Reading */
            case 'R':
            {
                if (stringLength((char*)line+2) < 12)
                {
                    uint8_t fileStatus = FR_INT_ERR;
                    if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                    {
                        stringCopy(readFileName,(char*)line+2);
                        sendFileCommand('R',13,line+2);
                        xQueueReceive(fileReceiveQueue,&readFileHandle,portMAX_DELAY);
                        sendResponse("fR",readFileHandle);
                        xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                        xSemaphoreGive(fileSendSemaphore);
                    }
                    sendResponse("fE",(uint8_t)fileStatus);
                }
                break;
            }
/**
<li> <b>Ghh</b> hh is the file handle. Close file for write or read file. The
file handle is a two character integer. */
            case 'C':
            {
                uint8_t fileStatus = FR_INT_ERR;
                if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                {
                    uint8_t fileHandle = asciiToInt((char*)line+2);
                    sendFileCommand('C',1,&fileHandle);
                    xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                    if (fileStatus == FR_OK)
                    {
                        if (writeFileHandle == fileHandle)
                        {
                            writeFileHandle = 0xFF;
                            writeFileName[0] = 0;
                        }
                        else if (readFileHandle == fileHandle)
                        {
                            readFileHandle = 0xFF;
                            readFileName[0] = 0;
                        }
                    }
                    xSemaphoreGive(fileSendSemaphore);
                }
                sendResponse("fE",(uint8_t)fileStatus);
                break;
            }
/**
<li> <b>Ghh</b> hh is the file handle. Read a record of data from the read or
write file. The data starts from the end of the previous block that was read
(or the file start if just opened). The file handle is a two character integer.
A block of bytes is read from the file and stored in a circular buffer. A record
is taken from this block and sent, the rest remains in the buffer until the next
request. */
#define GET_RECORD_SIZE 80
            case 'G':
            {
                uint8_t fileStatus = FR_INT_ERR;
                if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                {
                    int numberRecords = asciiToInt((char*)line+2);
                    if (numberRecords < 1) numberRecords = 1;
                    static FRESULT fileStatus = FR_OK;
                    static uint8_t buffer[GET_RECORD_SIZE];
                    static uint8_t readPointer = 0;
                    static uint8_t writePointer = 0;
                    char sendData[GET_RECORD_SIZE];
                    uint8_t sendPointer = 0;
                    uint8_t fileHandle = asciiToInt((char*)line+2);
                    uint8_t blockLength = GET_RECORD_SIZE-1;
                    uint8_t numRead;
                    uint8_t parameters[2] = {fileHandle, blockLength};
                    while (numberRecords > 0)
                    {
/* The buffer is empty, so fill up. */
                        if (readPointer == writePointer)
                        {
                            sendFileCommand('G',2,parameters);
                            numRead = 0;
                            xQueueReceive(fileReceiveQueue,&numRead,portMAX_DELAY);
/* As records are written in entirety, premature EOF should not happen. */
                            if (numRead != blockLength)
                            {
                                fileStatus = FR_DENIED;
                                break;
                            }
                            uint8_t i;
/* Read the entire block to the local buffer. */
                            for (i=0; i<numRead; i++)
                            {
                                uint8_t nextWritePointer = (writePointer+1)
                                                % GET_RECORD_SIZE;
                                xQueueReceive(fileReceiveQueue,
                                    buffer+writePointer,portMAX_DELAY);
                                writePointer = nextWritePointer;
                            }
/* Get status byte. */
                            xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                        }
/* Assemble the data message until EOL encountered, or block exhausted. */
                        while (sendPointer < GET_RECORD_SIZE-1)
                        {
                            sendData[sendPointer] = buffer[readPointer];
                            readPointer = (readPointer+1) % GET_RECORD_SIZE;
                            if (sendData[sendPointer] == '\n')
                            {
                                sendData[sendPointer+1] = 0;
                                sendString("fG",sendData);
                                sendPointer = 0;
                                numberRecords--;
                                break;
                            }
/* If the current block is exhausted, go get some more. */
                            if (readPointer == writePointer) break;
                            sendPointer++;
                        }
                    }
                    xSemaphoreGive(fileSendSemaphore);
                }
/* Status sent is from the last time the file was read. */
                sendResponse("fE",(uint8_t)fileStatus);
                break;
            }
/**
<li> <b>Dd</b> Get a directory listing d=dirname. Directory name is 8.3 string
style. Gets all items in the directory and sends the type,size and name, each
group preceded by a comma. The file command requests each entry in turn,
terminated by a null filename when the directory listing is exhausted. */
            case 'D':
            {
                if (! xSemaphoreTake(commsSendSemaphore,COMMS_SEND_TIMEOUT))
                    break;
                uint8_t fileStatus = FR_INT_ERR;
                if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                {
                    char firstCharacter;
                    sendFileCommand('D',13,line+2);
                    commsPrintString("fD");
                    do
                    {
                        char type = 0;
/* Single character entry type */
                        xQueueReceive(fileReceiveQueue,&type,portMAX_DELAY);
                        char character;
/* Four bytes of file size */
                        uint32_t fileSize = 0;
                        uint8_t i;
                        for (i=0; i<4; i++)
                        {
                            character = 0;
                            xQueueReceive(fileReceiveQueue,&character,portMAX_DELAY);
                            fileSize = (fileSize << 8) + character;
                        }
/* Filename. If the first character of name is zero then the listing is ended */
                        character = 0;
                        xQueueReceive(fileReceiveQueue,&character,portMAX_DELAY);
                        firstCharacter = character;
                        if (firstCharacter > 0)
                        {
                            commsPrintString(",");
                            commsPrintChar(&type);
                            commsPrintHex(fileSize >> 16);
                            commsPrintHex(fileSize & 0xFFFF);
                            while (character > 0)
                            {
                                commsPrintChar(&character);
                                character = 0;
                                xQueueReceive(fileReceiveQueue,&character,portMAX_DELAY);
                            }
/* End of directory entry. Discard the status byte */
                            xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                            uint8_t eol = 0;
/* Send a zero parameter to ask for the next entry */
                            sendFileCommand('D',1,&eol);
                        }
                    }
                    while (firstCharacter > 0);
                    commsPrintString("\r\n");
                    xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                    xSemaphoreGive(fileSendSemaphore);
                }
                xSemaphoreGive(commsSendSemaphore);
                sendResponse("fE",(uint8_t)fileStatus);
                break;
            }
/**
<li> <b>d[d]</b> d is the d=directory name. Get the first (if d present) or next
entry in the directory. If the name has a zero in the first position, return the
next entry in the directory listing. Returns the type, size and name preceded by
a comma for compatibility with the full directory listing request. If there are
no further entries found in the directory, then an empty string is sent back. */
            case 'd':
            {
                if (! xSemaphoreTake(commsSendSemaphore,COMMS_SEND_TIMEOUT))
                    break;
                uint8_t fileStatus = FR_INT_ERR;
                if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                {
                    sendFileCommand('D',13,line+2);
                    commsPrintString("fd");
                    char type = 0;
/* Single character entry type */
                    xQueueReceive(fileReceiveQueue,&type,portMAX_DELAY);
                    char character;
/* Four bytes of file size */
                    uint32_t fileSize = 0;
                    uint8_t i;
                    for (i=0; i<4; i++)
                    {
                        character = 0;
                        xQueueReceive(fileReceiveQueue,&character,portMAX_DELAY);
                        fileSize = (fileSize << 8) + character;
                    }
/* Filename. If the first character of name is zero then the listing is ended */
                    character = 0;
                    xQueueReceive(fileReceiveQueue,&character,portMAX_DELAY);
                    if (character > 0)  /* Pull in remaining characters of name */
                    {
                        commsPrintString(",");
                        commsPrintChar(&type);
                        commsPrintHex(fileSize >> 16);
                        commsPrintHex(fileSize & 0xFFFF);
                        while (character > 0)
                        {
                            commsPrintChar(&character);
                            character = 0;
                            xQueueReceive(fileReceiveQueue,&character,portMAX_DELAY);
                        }
                    }
                    commsPrintString("\r\n");
                    xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                    xSemaphoreGive(fileSendSemaphore);
                }
                xSemaphoreGive(commsSendSemaphore);
                sendResponse("fE",(uint8_t)fileStatus);
                break;
            }
/**
<li> <b>M</b> Register (mount or remount) the SD card. */
            case 'M':
            {
                uint8_t fileStatus = FR_INT_ERR;
                if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                {
                    sendFileCommand('M',0,line+2);
                    xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                    xSemaphoreGive(fileSendSemaphore);
                }
                sendResponse("fE",(uint8_t)fileStatus);
                break;
            }
/**
<li> <b>s</b> Send a status message containing: software switches
(configData.config.recording), names of open files, with open write filename
first followed by read filename, or blank if files are not open. */
            case 's':
            {
                if (! xSemaphoreTake(commsSendSemaphore,COMMS_SEND_TIMEOUT))
                    break;;
                commsPrintString("fs,");
                commsPrintInt((int)getControls());
                commsPrintString(",");
                uint8_t writeStatus;
                commsPrintInt(writeFileHandle);
                commsPrintString(",");
                if (writeFileHandle < 0xFF)
                {
                    commsPrintString(writeFileName);
                    commsPrintString(",");
                }
                commsPrintInt(readFileHandle);
                if (readFileHandle < 0xFF)
                {
                    commsPrintString(",");
                    commsPrintString(readFileName);
                }
                commsPrintString("\r\n");
                xSemaphoreGive(commsSendSemaphore);
                break;
            }
/**
<li> <b>Xf</b> Delete a designated file f=filename. The file must not be open at
the time. A status is returned to signal a directory refresh. */
            case 'X':
            {
                uint8_t fileStatus = FR_INT_ERR;
                if (xSemaphoreTake(fileSendSemaphore,COMMS_FILE_TIMEOUT))
                {
                    sendFileCommand('X',13,line+2);
                    xQueueReceive(fileReceiveQueue,&fileStatus,portMAX_DELAY);
                    xSemaphoreGive(fileSendSemaphore);
                }
                sendResponse("fE",(uint8_t)fileStatus);
                break;
            }
/**
</ul> */
        }
    }