/****************************************************************************/
/* Copyright 2007 MBARI.                                                    */
/* Monterey Bay Aquarium Research Institute Proprietary Information.        */
/* All rights reserved.       																						  */
/*	 																																				*/
/* RS485.c - handles serial communications. Includes ISR for serial buffer  */
/*																																				  */
/* Rev History:																															*/
/* Initial Creation 11/2007		B.Kieft																		    */
/*																																					*/
/*																																					*/
/****************************************************************************/


#include <stdlib.h>


#ifndef RS485_DRIVER
#define RS485_DRIVER

#ifndef RS485_RX_PIN
#define RS485_RX_PIN       PIN_C7   // Data receive pin
#endif

#ifndef RS485_TX_PIN
#define RS485_TX_PIN       PIN_C6   // Data transmit pin
#endif

#ifndef RS485_RX_ENABLE
#define RS485_RX_ENABLE    PIN_B5   // Controls RE pin.  Should keep low.
#endif

#use rs232(baud=9600, xmit=RS485_TX_PIN, rcv=RS485_RX_PIN,bits=8, stream=RS485,errors, RESTART_WDT, DISABLE_INTS)//rcv
#define RS485_wait_time 20                     // Wait time in milliseconds
#bit    rs485_collision = rs232_errors.6

int new_message;

static const int incoming_485_buffer_len = 12; //  /aaccCPPPPP<cr lf> - where a=address, c= command, p=param. Capital = optional
static const int cmd_start_pos = 3;            // location in incoming buffer that would mark the beginning of a command
int8 rs485_InBuffer[incoming_485_buffer_len];
int index;

char* commandStr[3];            //To store incoming command string
char* paramStr[5];              //To store incoming parameter string          
int8 incoming_checksum = 0x00;  //To store incoming checksum
int8 to_address = 0;            //Initialize to impossible address
int32 parameter;                //To store the int parameter
Boolean Receiving = false;      //Have received a start of message char?
Boolean bad_command = false;    //Definately an invalid command (e.g. cmd is a numeric char)
Boolean bad_param = false;      //Definately an invalid command (e.g. param is an alpha char)
Boolean is_status_cmd = false;  //status command has two chars, others have only 1



// Purpose:    Enable data reception
// Inputs:     None
// Outputs:    None
void RCV_ON(void) {
  while(kbhit(RS485)) {fgetc(RS485);} // Clear RX buffer. Clear RDA interrupt flag. Clear overrun error flag. 
  output_low(RS485_RX_ENABLE);
}

// Purpose:    Disable data reception
// Inputs:     None
// Outputs:    None
void RCV_OFF(void) {
   output_high(RS485_RX_ENABLE);
}


// Purpose:    Initialize RS485 communication. Call this before
//             using any other RS485 functions.
// Inputs:     None
// Outputs:    None
#SEPARATE
void rs485_init() {
   //RS485_ID = 1; //*** DEBUG   
   RCV_ON();
   new_message=FALSE;
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);
}


// Purpose:    Send a message over the RS485 bus
// Inputs:     1) len
//             2) data (acknowlege, time to close)
// Outputs:    TRUE if successful
//             FALSE if failed
// Note:     
#SEPARATE
int1 rs485_send_message(int8 len, int8* data) {
   int8 try, i, cs;
   int1 ret = FALSE;

   RCV_OFF();

   for(try=0; try<5; ++try) {
      rs485_collision = 0;

      for(i=0; i<len; ++i) {
       //  cs ^= *data;
         fputc(*data, RS485);
         ++data;
      }
            
      if(!rs485_collision) {
      delay_ms(serial_delay_msec); 
         ret = TRUE;
         break;
      }
      delay_ms(serial_delay_msec);
   }
   RCV_ON();
   return(ret);
}


// Purpose:    Interrupt service routine for handling incoming RS485 data
// data format: See TBD interface spec
#int_rda
void incomming_rs485()
{
	   int i;
	   
	   // Check for start of message character
     rs485_InBuffer[index] = fgetc();
     if(rs485_InBuffer[index] == '/') 
     {
     	 // Initialize variables
       Receiving = true;	
       index = 0;
       
       // Clear the buffer...
       for(i=0; i < incoming_485_buffer_len; i++)
       {
         rs485_InBuffer[i] = null;	
       }
       rs485_InBuffer[0]= '/';
     }
     
     if(Receiving) 
     {
     	 // Check for message termination character and for minimum message length
       if( (rs485_InBuffer[index] == '\r') && (index > 4) ) //Smallest message is currently "/aaCE" where aa is the 485 address/ID
       {
         Receiving = false;  // Done receiving
         index = 0;          // Reset index 
         new_message=TRUE;   // Anncounce the receipt of a new message


         // Convert ascii to int for to_address
         if( (isdigit(rs485_InBuffer[1])) && (isdigit(rs485_InBuffer[2])) )
         {
           to_address = (((rs485_InBuffer[1] - '0')*10) + (rs485_InBuffer[2]- '0'));         
         }
         else
         {
           // Invalid address
           new_message=false;   
         }

//         //        
//         // Convert ascii to hex for incoming_checksum
//         //
//         if( (isxdigit(rs485_InBuffer[5])) && (isxdigit(rs485_InBuffer[6])) )
//          {
//           if (rs485_InBuffer[6] > '9') 
//            {
//             rs485_InBuffer[6] += 9;
//            }
//           rs485_InBuffer[6] &= 0x0F;
//
//           if (rs485_InBuffer[5] > '9') 
//            { 
//             rs485_InBuffer[5] += 9;
//            }
//          
//           rs485_InBuffer[5] &= 0x0F;
//           incoming_checksum =( (rs485_InBuffer[5]<<1)|(rs485_InBuffer[6]) );         
//          }
//         else
//          {
//           bad_checksum = true;    
//          }
                

        } // End of message processing
        
        index += 1;
        if(index > incoming_485_buffer_len) // Check to see if we've filled the buffer
        {
         Receiving = false;  // Stop receiving and...
         index = 0;          // Reset index        		
        }
      
      }

}


// Purpose:    Do more in depth checking on incoming 485 
//             buffer contents to see if a valid message 
//             has been received
// Inputs:     1) Boolean wait
//            
// Outputs:    TRUE if new message available
//             FALSE if not
// Note:     
#SEPARATE
int1 rs485_get_new_message(int1 wait)
{
  while(wait && (new_message==FALSE)) {}

  // if the ISR doesn't think a new message is in, return false
  if(new_message==FALSE)
  {
    return FALSE;
  }
  else			
  {
    //Check if message is for this address
    if( to_address == RS485_ID )		
   	{
      //First check for status commands
      if( (rs485_InBuffer[cmd_start_pos] == '?') && (isalpha(rs485_InBuffer[cmd_start_pos + 1])) && (isalpha(rs485_InBuffer[cmd_start_pos + 2])) ) 
      {
        sprintf(commandStr, "%c%c%c",rs485_InBuffer[cmd_start_pos] , rs485_InBuffer[cmd_start_pos + 1], rs485_InBuffer[cmd_start_pos + 2]);

   	    // Check for ground fault command here to get the channel number
   	    if( ((rs485_InBuffer[cmd_start_pos + 1] == 'F') && (rs485_InBuffer[cmd_start_pos + 2] == 'C')) || ((rs485_InBuffer[cmd_start_pos + 1] == 'f') && (rs485_InBuffer[cmd_start_pos + 2] == 'c'))  ) {
          // Check for a parameter that's a number
          if( isdigit(rs485_InBuffer[cmd_start_pos+3]) ) {
            sprintf(paramStr, "%c",rs485_InBuffer[cmd_start_pos+3]);
   	        parameter = atoi32(paramStr);   	
   	      } else {
   	      	parameter = -1;
   	      }   	
   	    } 
   	    new_message=FALSE;
   	    is_status_cmd = true;
        return TRUE;       	
      }
      // Then check for set or control commands
      else if( (isalpha(rs485_InBuffer[cmd_start_pos])) && (isalpha(rs485_InBuffer[cmd_start_pos+1])) )
      { 
      	// Grab the command
        sprintf(commandStr, "%c%c",rs485_InBuffer[cmd_start_pos], rs485_InBuffer[cmd_start_pos+1]);
        // Check for a parameter that's a number (or at least starts with one)
        if( isdigit(rs485_InBuffer[cmd_start_pos+2]) ) {
          sprintf(paramStr, "%c%c%c%c%c",rs485_InBuffer[cmd_start_pos+2],rs485_InBuffer[cmd_start_pos+3],rs485_InBuffer[cmd_start_pos+4],rs485_InBuffer[cmd_start_pos+5],rs485_InBuffer[cmd_start_pos+6]);
   	      parameter = atoi32(paramStr);
   	    } else {
   	    	parameter = -1;
   	    }
   	    new_message=FALSE;
   	    is_status_cmd = false;
        return TRUE;       	        	
      } 	
      else
      {
      	// No valid command received so return
        return false;
      }
  


    }
    //message not for this address. Clear new message flag and return false
    else	
    {
      new_message=FALSE;
      return FALSE;
    }
   
   
  } // end of "new message in" if block
   
}


// Purpose:    Wait for wait time for the RS485 bus to become idle
// Inputs:     TRUE - restart the watch dog timer to prevent reset
//             FALSE - watch dog timer not restarted
// Outputs:    None
#SEPARATE
void rs485_wait_for_bus(int1 clrwdt)
{
   int16 i;

   RCV_OFF();
   for(i=0; i <= (rs485_wait_time*20); ++i)
   {
      if(!input(RS485_RX_PIN))//this is inverted as in the original file
         i = 0;
      else
         delay_us(50);

      if(clrwdt)
         restart_wdt();
   }
	RCV_ON();
}

//*** DEBUG -- will be replaced eventually by send_485_message
//#SEPARATE
//void send_response(char* s)
//{
//   int8 size;
//   
//   for(size=0; s[size]!='\0'; ++size);
//
//   rs485_wait_for_bus(false);
//   while(!rs485_send_message(size, s))
//   {    
//    delay_ms(10);
//   }
//}
//*** DEBUG

// Purpose:    Sends 485 string in format compliant with interface spec
// Inputs:     Char * - data to send
//            
// Outputs:    None
#SEPARATE
void send_485_response(char* p)
{
  int8 size;
  char final_string[40];

  // Do error calculations and query the slave if necessary
  calculate_errors();

  // Form up the string to comply with interface spec
  sprintf(final_string, "*%02u%s%LX\r\n", RS485_ID, p, error_status);
  
  // Grab the size
  for(size=0; final_string[size]!='\0'; ++size);
 
  // And send it out
  rs485_wait_for_bus(false);
  while(!rs485_send_message(size, final_string))
  {    
   delay_ms(10);
  }
  
  // Finally, clear all non-persistent errors  
  clear_errors(false);
   
}

//#endif
