// --------------------------------------------------------------------------------------------
// Basic RF functions -- revised version 0.11
// designed for the Smart-Its Core
// RF-module: BIM2-433 (radiometrix), XTR434 (radio-tech)
// MCU: PIC16F876, PIC18F252

// --------------------------------------------------------------------------------------------
// last modified: 09.03.2003 

// --------------------------------------------------------------------------------------------
// COPYRIGHT (c) 2002, 2003 by Albrecht Schmidt - http://www.comp.lancs.ac.uk/~albrecht/
// Lancaster University, http://www.comp.lancs.ac.uk/
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// --------------------------------------------------------------------------------------------
// developed/improved for various prototypes 
// in the Smart-Its Project (http://www.smart-its.org) and
// in the Equator IRC (http://www.equator.ac.uk)

// --------------------------------------------------------------------------------------------
// compiled with
// CCS PCM C Compiler, Version 3.080
// CCS PCH C Compiler

// --------------------------------------------------------------------------------------------
// packet format
// <Preamble><FF><FF><10011001><LENByte><LENByte><...DATA...><CRC_LOW><CRC_HI>

// --------------------------------------------------------------------------------------------
// *********************************************************************************************
// you must define the following in your code!
// change these Pins if they are different in your design
// #define RF_TX_PIN PIN_C3
// #define RF_RX_PIN PIN_C0
// #define RF_CD_PIN PIN_B0
// #define RF_TX_ENABLE_PIN PIN_C2
// #define RF_RX_ENABLE_PIN PIN_C1
// #define FORCE_NEW_PROTOCOL 1  // or 0 to be compatible with the old protocol...
// #define RF_SPEED 19200        // or 38400
// *********************************************************************************************

// --------------------------------------------------------------------------------------------
// *********************************************************************************************
// high level functions:
// void reset_rf_buffer() - clear buffer, use before printf
// void to_rf_buffer(char c) - as first argument in printf 
//
// void RF_printf() - print the buffer over RF
// int  RF_printf_OnNOCD(long timeOut_ms) - print the buffer over RF if no carrier is detected
//
// int  rfReceiveOnCD(char *buf, int maxLen, long timeOut) - receive a packet via RF
//
// void RF_version() - print the version over RF
// *********************************************************************************************

// --------------------------------------------------------------------------------------------
// low level functions
// void RF_put_long(unsigned long ldata) - write a long to RF
// void RF_putc(char data) - write a char to RF
// char RF_getc() - get a char from RF
// int RF_kbhit() - check for a char on RF
//
// unsigned long crc16(char * msg, int len) - calculate a crc16 offline
//
// void rfPowerDown() - switch BIM2-module in power down mode
// void rfTxOn() - switch BIM2-module in transmission mode
// void rfRxOn() - switch BIM2-module in receive mode
// void rfSelfTest() - switch BIM2-module in self test mode
//
// int rfTransmit(char * msg, int len) - transmit a number of characters via RF
// int rfReceive(char *buf, int maxLen) - receive a packet via RF
// int rfReceiveOnCD(char *buf, int maxLen, long timeOut) - receive a packet via RF
//
// void RF_version() - print the version over RF

// --------------------------------------------------------------------------------------------
// important global variable:
// rf_buffer - a char array for communication
// int rf_buffer_ptr - the used size of the array rf_buffer

// maximal number of chars to sent/receive in one packet
#define MAXBUF 80 

#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 

// buffer used for printing to RF and for receiving packages
byte rf_buffer[MAXBUF];
// pointer to the position in the buffer
int rf_buffer_ptr;


// version number of this program
const float rf_version_no=0.11;

// the table for the CRC calculation
const unsigned long crc_tbl[16]=
{
	0x0000, 0xCC01,	0xD801,	0x1400,	0xF001,	0x3C00,	0x2800,	0xE401,
	0xA001,	0x6C00,	0x7800,	0xB401,	0x5000,	0x9C01,	0x8801,	0x4400
};

// reset the buffer pointer
void reset_rf_buffer()
{
	rf_buffer_ptr=0;
}

// fill the buffer with chars
// used as first parameter in printf
void to_rf_buffer(char c)
{ 
	if (rf_buffer_ptr<MAXBUF)
	{
  		rf_buffer[rf_buffer_ptr++]=c;
  	}
}

// write a long value to RF
void RF_put_long(unsigned long ldata)
{
#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 
	#byte ldataL = ldata
	#byte ldataH = ldata+1

	putc(ldataL); // low byte first
	putc(ldataH);
}

// write char to RF
void RF_putc(char data)
{
#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 
	putc(data);
}

// get a char from RF
// non blocking, returns the char received or 0x00 when timed out
// 0x00 does not garantee that it is a timeout could also be received
// however this will be detected in the CRC...
char RF_getc()
{
#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 
	boolean flag1;
	char ch;
	int i;
	flag1=0;
	
	while (flag1==0 && i<3000) // wait for the start id '10011001'
	{
		// check if a character is available
		if (kbhit())
		{
			// if yes then get
			ch=getc();
			flag1=1;
		} else {
			// else delay a bit
			restart_wdt();
			delay_us(5);
		}
		i++;
	}
	if (i==3000) 
	{
		// time out condition...
		return 0;
	}
	return ch;
}


// get a char from RF (blocking)
char RF_getc_blocking()
{
#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 
	return getc();
}


// get a char from RF
//boolean RF_kbhit()
int RF_kbhit()
{
#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 
	return kbhit();
}


// calculate a crc16 offline (for the functions below
// the code is woven in the transmit and receive function)
unsigned long crc16(char * msg, int len)
{
	int i, ch;
	unsigned long crc;
	// initialize
	crc = 0xFFFF;
	// go over the message
	for (i=0;i<len;i++)
	{
		ch = msg[i];
		/* Nibble-at-a-time processing. Note that because this is
		   using the reflected algorithm, the lo-nibble is processed first. */
		crc = crc_tbl[(ch ^ crc) & 15] ^ (crc >> 4);
		crc = crc_tbl[((ch >> 4) ^ crc) & 15] ^ (crc >> 4);
	}
	return crc;
}



// switch BIM2-module in power down mode
void rfPowerDown()
{
	// disable RX, disable TX
	output_high(RF_TX_ENABLE_PIN);
	output_high(RF_RX_ENABLE_PIN);
}

// switch BIM2-module in transmission mode
void rfTxOn()
{
	// disable RX, enable TX
	output_low(RF_TX_ENABLE_PIN);
	output_high(RF_RX_ENABLE_PIN);
}

// switch BIM2-module in receive mode
void rfRxOn()
{
	// enable RX, disable TX
	output_high(RF_TX_ENABLE_PIN);
	output_low(RF_RX_ENABLE_PIN);
}

// switch BIM2-module in self test mode
void rfSelfTest()
{
	// enable RX, enable TX
	output_low(RF_TX_ENABLE_PIN);
	output_low(RF_RX_ENABLE_PIN);
}

// transmit a number of characters via RF
// msg is a char array and len is the number of chars to transmitted
// always return 1 ...
int rfTransmit(char * msg, int len)
{
#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 
	int i;
	char ch;
	// for inline crc calculation
	unsigned long crc;
	crc = 0xFFFF;

	i=0;

	// transmitter on
	rfTxOn();
	restart_wdt();
	delay_ms(3); // according to the datasheet of BIM (old BIM1 version)

	//preample 18Byte ~ 180 bit ~ fastest case @57600bit/s ~ 3ms
	for (i=0;i<18;i++){
		RF_putc(85); // sent '01010101'
	}
	// two bytes with FF
	RF_putc(255);
	RF_putc(255);
	// indicate the start id '10011001' - selected by me 
	// no reason for the pattern...
	RF_putc(154);
	// sent len - number of data bytes to follow
	ch=len;
	RF_putc(ch);
	RF_putc(ch);
	// sent the message - be aware will perform poorly if data is unsymetric!!!
	for (i=0;i<len;i++)
	{
		restart_wdt();
		// copy the character
		ch = msg[i];
		// sent the char
		RF_putc(ch);
		// calc crc16
		crc = crc_tbl[(ch ^ crc) & 15] ^ (crc >> 4);
		crc = crc_tbl[((ch >> 4) ^ crc) & 15] ^ (crc >> 4);
	}
	// sent two bytes CRC - low byte first, the high byte
	RF_put_long(crc);
	rfPowerDown();
	
	return 1;
}

// receive a packet via RF
// buf is a char array and len is the max number of chars to receive 
// return 0 - OK received
// return 1 - Time out in receive loop
// return 3 - Packet len byte corrupted
// return 4 - Packet DATA corrupted 
int rfReceive(char *buf, int maxLen)
{
#use rs232(baud=RF_SPEED,xmit=RF_TX_PIN,rcv=RF_RX_PIN) 
	int j, packetLen, packetLen1;

	// storage for bytes returned
	byte  ch, crc0, crc1;
	// crc is used for inline CRC calculation, 
	// crc_rec is used to store the crc received in crc0 and crc1
	unsigned long i, crc, crc_rec;

	// initialize CRC
	crc = 0xFFFF;

	for (j=0;j<maxLen;j++) {buf[j]='X';}

	// switch the receiver on 
	rfRxOn();
	delay_ms(3); // according to the datasheet of BIM (old BIM1 version)

	ch=0;
	i=0;
	while (ch!=153 && ch!=154 && i<15000) // wait for the start id '10011001'
	{
		// check if a character is available
		if (RF_kbhit())
		{
			// if yes then get
			ch=RF_getc();
		} else {
			// else delay a bit
			restart_wdt();
			delay_us(5);
		}
		i++;
	}
	if (i==15000) 
	{
		// time out condition...
		return 1;
	}

	// got a start byte - get the data length next
	packetLen = RF_getc();
	if ((ch==154) ||(FORCE_NEW_PROTOCOL==1) ) { // newer version - safer packet size...
		packetLen1 = RF_getc();
		if (packetLen != packetLen1) { return 3;} // error in packet length...
	}
	// if the package is to long reduce the length
	// will result in a corrupt package...
	if ((packetLen+2) >= MAXBUF) 
	{
		// packet to long
		packetLen=MAXBUF - 2;
	}

	// read the data bytes now
	i=0;
	while ( i<packetLen){
		restart_wdt();
		// read a byte
		ch=RF_getc();
		// calc crc16
		crc = crc_tbl[(ch ^ crc) & 15] ^ (crc >> 4);
		crc = crc_tbl[((ch >> 4) ^ crc) & 15] ^ (crc >> 4);
		// store it in the buffer
		buf[i]=ch;
		i++;
	}
	// data bytes done, ... read the CRC
	crc0 = RF_getc();
	crc1 = RF_getc();
	// calc 16bit long from the bytes
	crc_rec = crc1*256+crc0;

	// put a zero at the end - make it printable for a string 
	buf[packetLen]=0;		

	// check the CRC...
	if (crc==crc_rec) {
		// packet ok
		return 0;
	} else {
		// packet currupt
		return 4;
	}
}

// print the buffer over RF
void RF_printf()
{
	rfTransmit(rf_buffer,rf_buffer_ptr);	
}

// print the buffer over RF
// timeOut_ms is the time in mili seconds that is looked for
// a time where there is no carrier, return a 0 if send was
// ok otherwise 1
int RF_printf_OnNOCD(long timeOut_ms)
{
	long i;
	rfRxOn();
	i=0;
	while(i<timeOut_ms){
		restart_wdt();
		if(input(RF_CD_PIN)==1){
		   	rfTransmit(rf_buffer,rf_buffer_ptr);	
		   	return 0; // ok
		} else {
		   delay_ms(1);
		}
		i++;
	}
	return 1; // timed out ....
}


// print the version over RF
void RF_version()
{
	reset_rf_buffer();
	printf(to_rf_buffer, "RF V%f, 09/03/03", rf_version_no);
	RF_printf();
}

// receive a packet via RF when a CD is dected
// buf is a char array and len is the max number of chars to receive 
// timeOut is the time in mili seconds that is looked for a carrier
// return 0 - OK received
// return 1 - Time out in receive loop
// return 2 - Time out NO_CD
// return 3 - Packet len byte corrupted
// return 4 - Packet DATA corrupted 
int rfReceiveOnCD(char *buf, int maxLen, long timeOut)
{
	long i;
	int ret;
 	rfRxOn();
	i=0;
	ret = 5;
	while(i<timeOut){
		restart_wdt();
		if(input(RF_CD_PIN)==0){
		   ret = rfReceive(buf,maxLen);
		   return (ret);
		} else {
		   delay_ms(1);
		}
		i++;
	}
	// timed out
	return 2;
}