/****************************************************************************/
/* Copyright 2006 MBARI.                                                    */
/* Monterey Bay Aquarium Research Institute Proprietary Information.        */
/* All rights reserved.                                                     */
/****************************************************************************/
//
//
//
//
//
//
//


#include <stdlib.h>


#ifndef RS485_DRIVER
#define RS485_DRIVER

int8 RS485_ID=0; 


#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, enable=RS485_ENABLE_PIN,bits=8, stream=RS485,errors)//rcv
#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

//**#ifndef RS485_RX_BUFFER_SIZE
//**#define RS485_RX_BUFFER_SIZE  32
//**#endif

//**int rs485_state, rs485_ni, rs485_no,new_message;
int new_message;
//**int8 rcv_state;


int8 rs485_InBuffer[30];
int8 cmd_InBuffer[1];
int index;
int Receiving = false;
int8 to_address = 0xFF;        //Initialize to impossible address
int8 incoming_checksum = 0x00; //To store incoming checksum


// 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);
}

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
void rs485_init() {
   RS485_ID= ( (!input(PIN_C3)<<3)|(!input(PIN_C2)<<2)|(!input(PIN_C1)<<1)|(!input(PIN_C0)) );
   RCV_ON();
//**   rs485_state=0;
//**   rs485_ni=0;
//**   rs485_no=0;
//**   rcv_state=0;
   new_message=FALSE;
   enable_interrupts(INT_RDA);
   enable_interrupts(GLOBAL);

}


// Purpose:    Send a message over the RS485 bus
// Inputs:     1) To address
//             2) From address
//			   3) len
//             4) data (acknowlege, time to close)
// Outputs:    TRUE if successful
//             FALSE if failed
// Note:       Format:  msg typ | source | data 
int1 rs485_send_message(int8 my_id, 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: from address, to address, length, command, data
#int_rda
void incomming_rs485()
{
     rs485_InBuffer[index] = getc();
     if(rs485_InBuffer[index] == '$') 
     	{
       Receiving = true;	
       index = 0;
       rs485_InBuffer[0]= '$';
      }
     
     if(Receiving) 
      {
       if(index == 4)
        {
         cmd_InBuffer[index - 4] = rs485_InBuffer[index];
        } 
       index += 1;
       
       if(index >=7) // Check for end of message
        {
         Receiving = false;  // Done receiving
         index = 0;          // Reset index 
         new_message=TRUE;   // Anncounce the receipt of a new message

         //
         // Convert ascii to hex for to_address
         //
         if( (isxdigit(rs485_InBuffer[1])) && (isxdigit(rs485_InBuffer[2])) )
          {
           if (rs485_InBuffer[2] > '9') 
            {
             rs485_InBuffer[2] += 9;
            }
             rs485_InBuffer[2] &= 0x0F;

            if (rs485_InBuffer[1] > '9') 
             { 
              rs485_InBuffer[1] += 9;
             }
            
            rs485_InBuffer[1] &= 0x0F;
            to_address =( (rs485_InBuffer[1]<<1)|(rs485_InBuffer[2]) );         
          }
         else
          {
           bad_address = true;    
          }

         //        
         // 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
      }
     // Turn receive back on
   	//** RCV_ON();
}


int1 rs485_get_new_message(int8 my_ID, int1 wait)
{
   while(wait && (new_message==FALSE)) {}

   if(new_message==FALSE)
      return FALSE;
   else			
    {
   	  if( (to_address == RS485_ID) && (!bad_address) )		//check if message is for this address
   	  {
      new_message=FALSE;					//clear new_message flag
      return TRUE;}
      else	//message not for my address, clear new message flag but don't copy input data
      {
      bad_address = false;
      new_message=FALSE;
      return FALSE;}
   }
}



// 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
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();
}

void send_response(char* s)
{
   int8 size;
   char* header[20];
 //  
   output_bit(PIN_B1, HI); // Turn on TX LED

   sprintf(header, "*%x",RS485_ID);
   s = strncat(header, s, 16);

   for(size=0; s[size]!='\0'; ++size);

   rs485_wait_for_bus(false);
   while(!rs485_send_message(RS485_ID,size, s))
   {    
    delay_ms(10);
    }
   output_bit(PIN_B1, LO); // Turn off TX LED
}

void send_error_response(char* s)
{
   int8 size;
   char* header[20];
   sprintf(header, "?%x ",RS485_ID);
   s = strncat(header, s, 16);

   for(size=0; s[size]!='\0'; ++size);

   rs485_wait_for_bus(false);
   while(!rs485_send_message(RS485_ID,size, s))
   {    
    delay_ms(10);
    }
}

//#endif
