Libraries and Example of mbed parallel bus using I2C port expanders

Dependencies:   HDSP253X mbed PCF8574_Bus

Host_Coms.cpp

Committer:
wim
Date:
2011-08-26
Revision:
5:38b853bb1afa

File content as of revision 5:38b853bb1afa:

/* Host_Coms - Communications with Host PC
 *
 * Copyright (c) 2011 Wim Huiskamp
 *
 * Released under the MIT License: http://mbed.org/license/mit
 *
 * version 0.2 Initial Release
*/
#include "mbed.h"
#include "Host_Coms.h"


/** Create a Host Coms object connected to the proper channel
 *
 * @param  Serial serial channel to connect to 
 * @brief 
*/
Host_Coms::Host_Coms(Serial &hostChannel) : _hostChannel(hostChannel) {
  
  _init();  
}   


/** Send a formatted string to the Host PC
 * @param
 * @returns int message length
 */
int Host_Coms::sendString(char * format, ...) {
   va_list args;
   va_start (args, format);
   int rv;

   rv = _hostChannel.printf(format, args);

   va_end (args);

   return rv;
}



/** Send a formatted message to the Host PC
 * @param
 * @returns int message length
 */
int Host_Coms::sendMessage(char * format, ...) {
   va_list args;
   va_start (args, format);
   int rv=vsprintf (_sendBuffer, format, args);

//   _hostChannel.printf("%s:len=%d\r\n", _sendBuffer, strlen(_sendBuffer));
   
   _hostChannel.printf("%s*%02X\r\n", _sendBuffer, _calcChecksum());

   va_end (args);

   return (rv + 5);  // length + * + checksum + <CR> + <NL>
}

/** Compute the XOR checksum on the message
 * @param
 * @returns int checksum
 */
int Host_Coms::_calcChecksum() {
  int checksum = 0;
  int i, last;

  //_sendBuffer[0] should be '$' and is ignored in checksum  
  last = strlen(_sendBuffer);
  for (i=1; i<last; i++) {
    checksum ^= (int) _sendBuffer[i];
  }  
  
  return checksum;
}


/** Parse the LFRNG message and decode the fields
 * @param
 * @returns bool valid message 
 * @brief
 */
bool Host_Coms::_parseLFRNG(char *data) {
   char *token;
   int tokenfield;
   bool result=true;
   
//   char hostmessage[] = "1234,5678,0";
//   strcpy(hostmessage, "1234,5678,0");
      
   token = strtok(data, ","); // get first token, separator is ','
   tokenfield = 1;   

   while ((token != NULL) && (tokenfield <= 3)) {     
     switch (tokenfield) {
        case 1:
                // Range First
                _hostChannel.printf("#%d: %s\r\n", tokenfield, token);
                
                // Convert and perform sanity check
                rangeFirst = atoi(token);
                if ((rangeFirst < 0) || (rangeFirst > 9999)) {
                  rangeFirst = 0;
                  result=false;
                }
                _hostChannel.printf("#%d: int=%04d\r\n", tokenfield, rangeFirst);                
                break;

        case 2:
                // Range Last
                _hostChannel.printf("#%d: %s\r\n", tokenfield, token);

                // Convert and perform sanity check
                rangeLast = atoi(token);
                if ((rangeLast < 0) || (rangeLast > 9999)) {
                  rangeLast = 0;
                  result=false;
                }

                _hostChannel.printf("#%d: int=%04d\r\n", tokenfield, rangeLast);                                
                break;

        case 3:
                // Multiple Returns
                _hostChannel.printf("#%d: %s\r\n", tokenfield, token);

                // Convert and perform sanity check
                mult = atoi(token);
                if ((mult < 0) || (mult > 1)) {
                  mult = 0;
                  result=false;
                }

                _hostChannel.printf("#%d: int=%1d\r\n", tokenfield, mult);                
                break;

        default:
                break;
     } // switch
     
     token = strtok(NULL, ","); // Get next token
     tokenfield++;     
   } // while

   // Could do additional sanity check on correct number of fields
      
   return result;
}

/** Parse the LFRES message and decode the fields
 * @param
 * @returns bool valid message
 * @brief
 */
bool Host_Coms::_parseLFRES(char *data) {
   char *token;
   int tokenfield;
   int STANAG_Code;
   bool result=true;
      
   token = strtok(data, ","); // get first token, separator is ','
   tokenfield = 1;   
   
   while ((token != NULL) && (tokenfield <= D_HOST_STANAG_CODES)) {   
     switch (tokenfield) {
        case 1:
        case 2:        
        case 3:        
        case 4:
        case 5:                        
        case 6:
        case 7:        
        case 8:        
        case 9:
        case 10:                        
        case 11:
        case 12:        
        case 13:        
        case 14:
        case 15:                        
        case 16:
        case 17:        
        case 18:        
        case 19:
        case 20:                        
                _hostChannel.printf("#%d: %s\r\n", tokenfield, token);
                
                // STANAG_Code[i] and sanity check
                STANAG_Code = atoi(token);
                if ((STANAG_Code < 0) || (STANAG_Code > 9999)) {
//                if (STANAG_Code < 0) || (STANAG_Code > 1999) {    // Check on limited code value            
                  STANAG_Code = 0;
                  result = false;
                }
                 
                STANAG_Codes[tokenfield - 1] = STANAG_Code;                 

                _hostChannel.printf("#%d: %s\r\n", tokenfield, STANAG_Codes[tokenfield - 1]);

                break;

        default:
                break;
     } // switch
     
     token = strtok(NULL, ","); // Get next token
     tokenfield++;     
   } // while
   
   // Could do additonal sanity check on correct number of fields
   
   return result;
}


/** Receive and Parse the Host message
 * @param
 * @returns Host_Mess message type that was received
 * @brief 
 *              Check LF28A Host channel and Parse received data for valid messages.
 *              Since the parser is a state machine, partial data may
 *              be supplied where the next time this method is called, the
 *              rest of the partial data will complete the message and 
 *              parsing will begin. A typical message is constructed as:
 *
 *                  $LFCMD,DDDD,DDDD,....DD*CS<CR><LF>
 *
 *              Where:
 *                        '$'            Hex 24 (Start of Message)
 *                        'LFCMD'        LF28A command or data
 *                        ',DDDD'        Zero or more data fields
 *                        '*CS'          Checksum field (optional)
 *                        <CR><LF>       Hex 0D 0A (End of Message)
 *
 *              When a complete message is received, this function sends the
 *              LF28A command and data to the parseCommand methods for
 *              individual field parsing. The received data will be stored
 *              in a datastructure. The return value informs the caller which
 *              command has been received to decide on proper action. 
 */
Host_Mess Host_Coms::parseMessage() {
  char data;
  static int cmdIdx = 0;                    // index for received command
  static int datIdx = 0;                    // index for received data
  static int computedChecksum = 0;          // computed from the received data   
  static int receivedChecksum = 0;          // checksum that was part of the received data
  static bool checksumReceived = false;     // received data included the optional checksum
  static bool messageComplete = false;      // message has been received that is ready for decoding
  Host_Mess   messageType;                  // valid decoded message type   
  static LFP_State parseState = LFP_STATE_SOM;
     
  // init while loop, no valid decoded message is available initially
  messageType = HOST_CMD_NONE;
  
  // Continue reading from host as long as data is available or until a
  // valid message has been received that needs to be processed first.
  while ((_hostChannel.readable()) && (messageType == HOST_CMD_NONE)) {
    data = _hostChannel.getc();

    // The main state machine which processes individual characters
    // until a complete message has been received that is ready for decoding.
    switch(parseState) {
      // Search for start of message '$'
      case LFP_STATE_SOM :
                            if(data == '$') {
                              cmdIdx = 0;                   // reset index
                              computedChecksum = 0;        // reset checksum
                              receivedChecksum = 0; 
                              messageComplete = false;     // restart                               
                              checksumReceived = false;    // restart                                                             
                              parseState = LFP_STATE_CMD;  // next state
                              
_hostChannel.printf("#start cmd\r\n");                              
                            }
  
  
                            break;

      // Retrieve command field
      case LFP_STATE_CMD :
                            switch (data) {
                              case ',':  
                                        // terminate command
                                        _command[cmdIdx] = '\0';

                                        // update checksum
                                        computedChecksum ^= (int) data;

                                        // goto get data state
                                        datIdx = 0;          // reset index                                                                                
                                        parseState = LFP_STATE_DATA;
_hostChannel.printf("#, end cmd\r\n");                                        
                                        break;
                              case '*':  
                                        // terminate command
                                        _command[cmdIdx] = '\0';

                                        // goto get checksum state 
                                        checksumReceived = true;                                                                               
                                        parseState = LFP_STATE_CS1;   
_hostChannel.printf("#* end cmd\r\n");
                                        break;                                        

                              case '\r':  
                              case '\n':                                
                                        // terminate command
                                        _command[cmdIdx] = '\0';
                                        
                                        // done, decode data and restart
                                        messageComplete = true;
                                        parseState = LFP_STATE_SOM;
_hostChannel.printf("#cr nl end cmd\r\n");
                                        break;
                                        
                              case '$' : 
                                        // unexpected restart 
                                        cmdIdx = 0;                     // reset index
                                        computedChecksum = 0;        // reset checksum 
_hostChannel.printf("#$ restart cmd\r\n");                                        
                                        break;

                              default :  
                                        // store command
                                        _command[cmdIdx++] = data;                                        
                                        
                                        // update checksum
                                        computedChecksum ^= (int) data;

                                        // Check for command overflow
                                        if (cmdIdx > D_HOST_MAX_CMD_LEN) {
                                          parseState = LFP_STATE_SOM;  // something went wrong, restart
                                        }
                                        
_hostChannel.printf("#in cmd\r\n");                                        
                                        break;                                                                               
                                        
                           } // switch (data)
                           break;

        // Store data and check for end of sentence or checksum flag
        case LFP_STATE_DATA :
                           switch (data) {
                              case '*':  
                                        // terminate data
                                        _data[datIdx] = '\0';

                                        // goto get checksum state
                                        checksumReceived = true;
                                        parseState = LFP_STATE_CS1;
_hostChannel.printf("#* end data\r\n");                                                                                                                                                                   
                                        break;                                        

                              case '\r':  
                              case '\n':                                
                                        // terminate data
                                        _data[datIdx] = '\0';
                                        
                                        // done, decode data and restart
                                        messageComplete = true;
                                        parseState = LFP_STATE_SOM;
_hostChannel.printf("#cr nl end data\r\n");                                                                                    
                                        break;

                              case '$' : 
                                        // unexpected restart 
                                        cmdIdx = 0;                     // reset index
                                        computedChecksum = 0;        // reset checksum 

                                        parseState = LFP_STATE_CMD;  // restart                                      
_hostChannel.printf("#$ in data, restart cmd\r\n");                                        
                                        break;
                                        
                              default :  
                                        // store data
                                        _data[datIdx++] = data;                                        

                                        // update checksum
                                        computedChecksum ^= (int) data;
                                        
                                        // Check for data overflow
                                        if (datIdx > D_HOST_MAX_DATA_LEN) {
                                          parseState = LFP_STATE_SOM;  // something went wrong, restart
                                        }
                                        
_hostChannel.printf("#in data\r\n");                                                                                
                                        break;                                                                               
                                        
                           } // switch (data)
                           break;
        
        // Handle checksum (part1) from sentence
        case LFP_STATE_CS1 :
        
                           if ((data >= '0') && (data <= '9')) {
                             receivedChecksum = (data - '0') << 4;
                             parseState = LFP_STATE_CS2;
                           }
                           else if ((data >= 'a') && (data <= 'f')) {
                             receivedChecksum = ((data - 'a') + 10) << 4;
                             parseState = LFP_STATE_CS2;                             
                           }
                           else if ((data >= 'A') && (data <= 'F')) {
                             receivedChecksum = ((data - 'A') + 10) << 4;
                             parseState = LFP_STATE_CS2;                             
                           }
                           else {
                             // not a valid checksum
                             parseState = LFP_STATE_SOM;  // something went wrong, restart
                           }
                      
                           break;

        // Handle checksum (part2) from sentence
        case LFP_STATE_CS2 :
        
                           if ((data >= '0') && (data <= '9')) {
                             receivedChecksum |= (data - '0');
                             
                             // done, decode data and restart
                             messageComplete = true;
                             parseState = LFP_STATE_SOM;                            
                           }
                           else if ((data >= 'a') && (data <= 'f')) {
                             receivedChecksum |= (data - 'a') + 10;

                             // done, decode data and restart
                             messageComplete = true;
                             parseState = LFP_STATE_SOM;                            
                           }
                           else if ((data >= 'A') && (data <= 'F')) {
                             receivedChecksum |= (data - 'A') + 10;
                             parseState = LFP_STATE_SOM;                             

                             // done, decode data and restart
                             messageComplete = true;
                             parseState = LFP_STATE_SOM;                                                        
                           }
                           else {
                             // not a valid checksum
                             parseState = LFP_STATE_SOM;  // something went wrong, restart
                           }
                      
                           break;
        

        // Something went wrong, restart
        default :
                           parseState = LFP_STATE_SOM;    
    } // switch (_parseState)
    
    
    // Check if done with this message
    if (messageComplete) {
_hostChannel.printf("#complete\r\n");                                                                                    
      if (!checksumReceived) {      
        // No checksum received, process message
        messageType = _parseCommand(_command, _data);     
      }
      else if (computedChecksum == receivedChecksum) {        
        // Correct checksum, process message              
        messageType = _parseCommand(_command, _data);
      }
      else {
       // Ignore messages with an invalid checksum
        messageType = HOST_CMD_NONE;               
      }
      
      // done processing this received message
      messageComplete = false;
    }

                                
  } // while readable and no valid message available

  
  // inform the caller when a valid message is available
  return messageType;
} // _parseMessage


/** Receive and Parse the Host command from the message
 * @param
 * @returns Host_Mess valid command type
 * @brief 
 *          Process LFCMD message - Use the _command and _data strings to call the
 *          appropriate command sentence data processor.
 *          Return which command has been received to let caller decide on proper action !!
*/
Host_Mess Host_Coms::_parseCommand(char *command, char *data) {
  Host_Mess result = HOST_CMD_NONE; 

  //
  // LFRES
  //
  if (strcmp(command, "LFRES") == NULL) {
    if (_parseLFRES(data))
      result = HOST_CMD_RES;
  }
  //
  // LFRNG
  //
  else if (strcmp(command, "LFRNG") == NULL) {
    if (_parseLFRNG(data))
      result = HOST_CMD_RNG;
  } 

  return result;
}



/** Init Host_Coms
 * @param
 * @returns 
 */
void Host_Coms::_init(void) {
  int i;
  
  rangeFirst=0;
  rangeLast=0;
  mult=0;
  
  for (i=0; i<D_HOST_STANAG_CODES; i++) {
    STANAG_Codes[i]=0;
  }  
}