/*! * Display the menu serial message with the maximum, minimum and current values */ static void dispSetValueMessage(void) { int num = 0; Direction dir; sendROM("\t"); sendROM(m_currentMenu.serialMessage); sendNewLine(1); // Enter a value between x and y sendROM("\t"); sendROM(inputNumRangeStr); transmit(intToAscii(m_currentMenu.minVal)); sendROM(and); transmit(intToAscii(m_currentMenu.maxVal)); sendNewLine(1); // The current value is: sendROM("\t"); sendROM(currentValueStr); switch (m_currentMenu.menuID) { case AZ_GOTO: dir = getDir(); num = (int) dir.azimuth; break; case EL_GOTO: dir = getDir(); num = (int) dir.elevation; break; case AZ_MIN: num = getMinAzimuthAngle(); break; case AZ_MAX: num = getMaxAzimuthAngle(); break; case EL_MIN: num = getMaxAzimuthAngle(); break; case EL_MAX: num = getMaxAzimuthAngle(); break; case RANGE_MIN: num = getMinRange(); break; case RANGE_MAX: num = getMaxRange(); break; case US_SAMPLE_RATE: num = getUsSampleRate(); break; case US_SAMPLE_AVG: num = getNumSamples(); break; } transmit(intToAscii(num)); sendNewLine(1); }
/*! * Description: Displays a number out of range error */ static void errOutOfRange(int lowerBound, int upperBound) { // sendROM(errStr); sendROM(inputNumRangeStr); transmit(intToAscii(lowerBound)); sendROM(and); transmit(intToAscii(upperBound)); sendNewLine(1); }
/*! * Display the Show Temperature serial message */ static void dispTempSerialMessage(void) { int temp = readTemp(); sendROM("\t"); sendROM(showTemp1); transmit(intToAscii(temp)); sendROM(showTemp2); }
bool dataMessageSend(char* ident, int32_t param1, int32_t param2) { char param1Buffer[11]; char param2Buffer[11]; intToAscii(param1,param1Buffer); intToAscii(param2,param2Buffer); if (buffer_output_places(sendBuffer) < stringLength(param1Buffer)+stringLength(param2Buffer)+5) return false; commsPrintString(ident); commsPrintString(","); commsPrintString(param1Buffer); commsPrintString(","); commsPrintString(param2Buffer); commsPrintString("\r\n"); return true; }
void killLatest() { if (existingProcesses==0) write(STDOUT_FILENO,"No processes to kill\n",21); else { char num[10];intToAscii(pList[existingProcesses-1].pid, num); if (existingProcesses!=0) existingProcesses--; write(STDOUT_FILENO,num,6);write(STDOUT_FILENO," process has been issued kill signal. May already be killed\n",61); kill(latestPID,SIGKILL); }//else }//killLatest
bool sendResponse(char* ident, int32_t parameter) { char paramBuffer[11]; intToAscii(parameter,paramBuffer); if (buffer_output_places(sendBuffer) < stringLength(paramBuffer)+4) return false; commsPrintString(ident); commsPrintString(","); commsPrintString(paramBuffer); commsPrintString("\r\n"); return true; }
void displayProcesses() { int i=0; if (existingProcesses==0) write(STDOUT_FILENO,"No processes were run\n",22); else { for(i=0;i<existingProcesses;i++) { char num[10]; intToAscii(pList[i].pid, num); write(STDOUT_FILENO,"Processes that were run:\n",25); write(STDOUT_FILENO,num,strlen(num));write(STDOUT_FILENO,"= process id\n",13); //intToAscii(pList[i].running, num); //write(STDOUT_FILENO,num,strlen(num));write(STDOUT_FILENO,"=status\n",8); }//for }//else }//displayProcesses
void passGenerator(int *pA, char *pCA, char *res) { for (int i=1; i<55; i++) { if ((i%5) == 0) { *res = '_'; printf("1. res = %c\n", *res); } else if ((i%2) == 0) { *res = intToAscii(*pA); pA++; printf("2. res = %c\n", *res); } else { *res = *pCA; pCA++; printf("3. res = %c\n", *res); } printf("i = %i\n", i); res++; } }
/*! * Description: General funtion for menus which set values * (Such as Set Max Range). This calls the * appropriate function, and transmits user messages. */ static void setValue(int input) { char string[20] = " set to \0"; char stringLcd[20] = {0}; Direction dir; // Sends a new line sendNewLine(1); // Handle inpropper input values if (input < m_currentMenu.minVal || input > m_currentMenu.maxVal) { errOutOfRange(m_currentMenu.minVal, m_currentMenu.maxVal); sendNewLine(1); return; } // Handle Different cases // @TODO handle current values // @TODO Integrate switch (m_currentMenu.menuID) { case AZ_GOTO: dir.azimuth = input; dir.elevation = getDir().elevation; move(dir); sendROM(angleStr); break; case EL_GOTO: dir.azimuth = getDir().azimuth; dir.elevation = input; move(dir); sendROM(angleStr); break; case AZ_MIN: setMinAzimuthAngle((char) input); AzGoto.minVal = input; sendROM(angleStr); break; case AZ_MAX: setMaxAzimuthAngle((char) input); AzGoto.maxVal = input; sendROM(angleStr); break; case EL_MIN: setMinElevationAngle((char) input); ElGoto.minVal = input; sendROM(angleStr); break; case EL_MAX: setMaxElevationAngle((char) input); ElGoto.maxVal = input; sendROM(angleStr); break; case RANGE_MIN: setMinRange(input); sendROM(mmStr); break; case RANGE_MAX: setMaxRange(input); sendROM(mmStr); break; case US_SAMPLE_RATE: setUsSampleRate(input); sendROM(sampleRate); break; case US_SAMPLE_AVG: setNumSamples(input); sendROM(numPerSample); break; } // Transmit the final part of the sentence transmit(string); transmit(intToAscii(input)); sendNewLine(1); //lcdWriteString(strcpypgm2ram(stringLcd, "OK!"), 2); }
/*! ********************************************************************** * Function: serviceMenu(void) * * \brief services any user interface with the menu * * Include: * * Description: Checks if the user has made any inputs to the system. If not * the function simply returns. If they have then it services * the inputs, displays the correct outputs and performs the * specified actions * * Arguments: None * * Returns: None *************************************************************************/ void serviceMenu(void) { char buttonInput; char string[50]; int numericInpt; // Go to factory mode if (FACTORY_SWITCH == 1 && m_userMode == REMOTE) { m_userMode = FACTORY; setMenu(m_currentMenu); } // Rest back to serial mode else if(FACTORY_SWITCH == 0 && m_userMode == FACTORY) { m_userMode = REMOTE; setMenu(topMenu); } if (m_userMode == REMOTE || m_userMode == FACTORY) { // Check for serial input if (checkForSerialInput()) { // Handle serial input numericInpt = getSerialNumericInput(); sendNewLine(1); sendROM("DEBUG: Value entered = "); transmit(intToAscii(numericInpt)); sendNewLine(1); clearReceive(); } else { return; } } // else // { // if (userEmpty()) // { // // Display the current value on the LCD // // Get the Potentiometer result for the current menu // numericInpt = getLocalPotResult(m_currentMenu.minVal, m_currentMenu.maxVal, m_currentMenu.increment); // // Display string on LCD // m_currentMenu.lcdDisplayFunction(numericInpt); // return; // } // else // { // // Confirm the user's input // buttonInput = userPop(); // if (buttonInput == CONFIRM_CHAR) // { // numericInpt = getLocalPotResult(m_currentMenu.minVal, m_currentMenu.maxVal, m_currentMenu.increment); // } // else // { // // Back was pressed // numericInpt = ESC_PRESSED; // } // } // } /// If Esc or Back button pressed, return if (numericInpt == ESC_PRESSED) { m_currentMenu.returnToPrevious(); return; } else { /// Otherwise Confirm the selection m_currentMenu.confirmFunction(numericInpt); return; } }
void assignProcesses(char **s, int maxProcesses, int fg) { int p=0,ppid=0,pid=0,foreground=1; int fd[2]; int stat; pipe(fd); char *ps1[40],*ps2[40]; parsep(s[0],ps1); parsep(s[1],ps2); //0 is STDIN, 1 is STDOUT int pgid=getpid(); int success=0,invalid=0; if ((pid=fork())==0) { if (setpgid(pid, pgid)<0) write(STDOUT_FILENO,"Couldn't assign separate group id\n",34); if (fg==1) { if (tcsetpgrp(shellTerminal, pgid)<0) write(STDOUT_FILENO,"Couldn't put in separate process group\n",40); } signal(SIGINT,overideInt);//set the Ctrl-C signal handler signal(SIGTSTP,overideSuspend);//set the Ctrl-Z signal handler signal(SIGQUIT,overideQuit);//set the Ctrl-\ signal handler dup2(fd[1],1); if ((invalid=execvp(ps1[0],ps1))<0) {write(STDOUT_FILENO,"Invalid command\n",16);exit(1);} }//if else { close(fd[1]); if (invalid>=0) {latestPID=pid;addProcess(pid,pgid,1,0);} if (fg==1) waitpid(pid,&stat,0); else { if (invalid>=0) { char num[10];intToAscii((int)pid, num); write(STDOUT_FILENO,num,6); write(STDOUT_FILENO," process sent to background\n",28); }//if }//else }//else if ((pid=fork())==0) { if (setpgid(pid, pgid)<0) write(STDOUT_FILENO,"Couldn't assign separate group id\n",34); if (fg==1) {if (tcsetpgrp(shellTerminal, pgid)<0) write(STDOUT_FILENO,"Couldn't put in separate process group\n",40);} signal(SIGINT,overideInt);//set the Ctrl-C signal handler signal(SIGTSTP,overideSuspend);//set the Ctrl-Z signal handler signal(SIGQUIT,overideQuit);//set the Ctrl-\ signal handler dup2(fd[0],0); if ((invalid=execvp(ps2[0],ps2))<0) {write(STDOUT_FILENO,"Invalid command\n",16);exit(1);} }//if else { if (invalid>=0) {latestPID=pid;addProcess(pid,pgid,1,0);} if (fg==1) waitpid(pid,&stat,0); else { if (invalid>=0) { char num[10];intToAscii((int)pid, num); write(STDOUT_FILENO,num,6);write(STDOUT_FILENO," process sent to background\n",28);} } }//else }//assignProcess
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> */ } }