//University of Connecticut
//Spring 2004 Senior Design: Chris Greci, Cory Liu, Brian Saucier
//Wireless Sensor Network
//code for sensor nodes

// http://www.engr.uconn.edu/ece/SeniorDesign/projects/ecesd33/  for more information
 

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <chipcon/reg1010.h>
#include <chipcon/cc1010eb.h>
#include <chipcon/hal.h>
#include <chipcon/cul.h>
#include <chipcon/vt100.h>



// sensor packet:
#define NODE_ID_LENGTH      2 // word
#define NODE_NAME_LENGTH    20
#define TEMP_OFFSET         (NODE_ID_LENGTH + NODE_NAME_LENGTH)
#define TEMP_LENGTH         2
#define MOTION              2
#define SOUND               2
#define MOTION_OFFSET		(NODE_ID_LENGTH + NODE_NAME_LENGTH + TEMP_LENGTH)
#define SOUND_OFFSET        (NODE_ID_LENGTH + NODE_NAME_LENGTH + TEMP_LENGTH + MOTION)
#define DATA_LEN            (NODE_ID_LENGTH + NODE_NAME_LENGTH + TEMP_LENGTH + MOTION+SOUND)

// Radio related:
#define MY_SPP_ADDRESS      3
#define RX_INTERVAL         50
#define PREAMBLE_COUNT      4

// Node registration
#define INVALID_NODE_INDEX  255
#define UNUSED_NODE_ID      0x0000

// Speed related
byte xdata waitMultiplier;

// The temperature "table":
#define MAX_NODE_COUNT      16
word xdata nodeIDs[MAX_NODE_COUNT];
byte xdata nodeNames[MAX_NODE_COUNT][NODE_NAME_LENGTH];
word xdata nodeTemps[MAX_NODE_COUNT];
word xdata nodeLastT[MAX_NODE_COUNT];

// SPP variables
SPP_SETTINGS xdata sppSettings;
SPP_RX_INFO xdata RXI;
SPP_TX_INFO xdata TXI;
SPP_TX_INFO xdata TXIR;
byte xdata rxDataBuffer[DATA_LEN];
byte xdata txDataBuffer[DATA_LEN];

// Function prototypes
void WaitRandom (void);
void Transmit (void);
void Receive (void);
void PrintTable (void);
void relay (void);

// Unit name, stored in Flash
byte code flashUnitName[NODE_NAME_LENGTH];

// RAM buffer for Flash copy
byte xdata ramBufNonAligned[128];




//----------------------------------------------------------------------------
//	MAIN PROGRAM
//----------------------------------------------------------------------------
void main (void) {

    byte xdata n;
    byte xdata m;




#ifdef FREQ433


// X-tal frequency: 14.745600 MHz
// RF frequency A: 433.302000 MHz	Rx
// RF frequency B: 433.302000 MHz	Tx
// RX Mode: Low side LO
// Frequency separation: 64 kHz
// Data rate: 19.2 kBaud
// Data Format: NRZ
// RF output power: 10 dBm
// IF/RSSI: RSSI Enabled

RF_RXTXPAIR_SETTINGS code RF_SETTINGS = {
    0xA3, 0x2F, 0x0E,    // Modem 0, 1 and 2
    0x58, 0x00, 0x00,    // Freq A
    0x41, 0xFC, 0x9C,    // Freq B
    0x02, 0x80,          // FSEP 1 and 0
    0x60,                // PLL_RX
    0x48,                // PLL_TX
    0x44,                // CURRENT_RX
    0x81,                // CURRENT_TX
    0x0A,                // FREND
    0xFF,                // PA_POW
    0xC0,                // MATCH
    0x00,                // PRESCALER
    };

#endif

    // Calibration data
    RF_RXTXPAIR_CALDATA xdata RF_CALDATA;
    
    // Initialize peripherals
    WDT_ENABLE(FALSE);   //watchdog timer off
    RLED_OE(TRUE);
    YLED_OE(TRUE);
    GLED_OE(TRUE);
    BLED_OE(TRUE);

    // Startup macros for speed and low power consumption
    MEM_NO_WAIT_STATES();
    FLASH_SET_POWER_MODE(FLASH_STANDBY_BETWEEN_READS);

    // Seed the random generator:
    halRandomNumberGen(&n, 1);
    halRandomNumberGen(&m, 1);
    srand((n << 8) + m);

    waitMultiplier = 1;

    
    
	//ADC_POWER(TRUE);

    // RF/SPP setup
    sppSetupRF(&RF_SETTINGS, &RF_CALDATA, TRUE);
    sppSettings.myAddress = MY_SPP_ADDRESS;
    sppSettings.rxTimeout = RX_INTERVAL;
    sppSettings.txAckTimeout = PREAMBLE_COUNT;
    sppSettings.txPreambleByteCount = PREAMBLE_COUNT;
    RXI.maxDataLen = DATA_LEN;
    RXI.pDataBuffer = rxDataBuffer;
    TXI.destination = SPP_BROADCAST;
    TXI.flags = 0x00;
    TXI.pDataBuffer = txDataBuffer;
    TXI.dataLen = DATA_LEN;



	// our packet struct for relaying,everything is left the same but the 
	// recieved data is going into the tx buffer
	TXIR.pDataBuffer = rxDataBuffer;
	TXIR.destination = SPP_BROADCAST;
    TXIR.flags = 0x00;
	TXIR.dataLen = DATA_LEN;

    // Initialize the SPP timer
    sppStartTimer(CC1010EB_CLKFREQ);
    SPP_INIT_TIMEOUTS();

    // Reset the node IDs
    for (n = 0; n < MAX_NODE_COUNT; n++) {
        nodeIDs[n] = UNUSED_NODE_ID;
    }

    // Reset our name buffer
    for (n = 0; n < NODE_NAME_LENGTH; n++) {
        nodeNames[0][n] = 0x00;
    }

    // Setup UART0 for polled I/O 
    UART0_SETUP(57600, CC1010EB_CLKFREQ, UART_NO_PARITY | UART_RX_TX | UART_POLLED);

	// ADC setup
    //halConfigADC(ADC_MODE_SINGLE | ADC_REFERENCE_INTERNAL_1_25, CC1010EB_CLKFREQ, 0);
    //ADC_SELECT_INPUT(ADC_INPUT_AD1);

    // Get our name
    VT100_CLEAR_SCREEN();
    VT100_GO_TOP_LEFT();

    // Load name from Flash
    memcpy(&nodeNames[0][0],flashUnitName,NODE_NAME_LENGTH);
    
    // Get our ID from CRC16(our name)
    nodeIDs[0] = culFastCRC16Block(&nodeNames[0][0], NODE_NAME_LENGTH, CRC16_INIT);

    // Prepare the id+name part of the packet
    txDataBuffer[0] = (nodeIDs[0] >> 8) & 0xFF;
    txDataBuffer[1] = nodeIDs[0] & 0xFF;
    
    for (n = 0; n < NODE_NAME_LENGTH; n++) {
        txDataBuffer[n + NODE_ID_LENGTH] = nodeNames[0][n];
    }


	// Setup external interrupt 0 as an interrupt source
    INT_EXTERNAL0_TRIGGER_ON_LEVEL();
    INT_PRIORITY(INUM_EXTERNAL0, INT_HIGH);
    INT_ENABLE(INUM_EXTERNAL0, INT_ON);

    // Setup external interrupt 1 as an interrupt source
    INT_EXTERNAL1_TRIGGER_ON_LEVEL();
    INT_PRIORITY(INUM_EXTERNAL1, INT_LOW);
    INT_ENABLE(INUM_EXTERNAL1, INT_ON);
	

	
	// port 2.4 is a switch on the sensorboard, used to turn on or off relay functionality
	// Setup ADC interrupt
	if(PORTBIT(2, 4) == TRUE)	
    { 
		//INT_ENABLE(INUM_ADC, INT_ON);
	
	}
    // Turn on interrupts
    INT_GLOBAL_ENABLE(INT_ON);



    // Loop forever
    while (TRUE) {
       
		// Enable 32kHz oscillator, wait 0.5s to stabilize, 
        // then switch clock source, then disable high-speed XOSC and ADC
		X32_INPUT_SOURCE(X32_USING_CRYSTAL);
        X32_ENABLE(TRUE);
        halWait(250, CC1010EB_CLKFREQ);
        halWait(250, CC1010EB_CLKFREQ);
        MAIN_CLOCK_SET_SOURCE(CLOCK_X32);
        XOSC_ENABLE(FALSE);
		ADC_POWER(FALSE);


		// Set IDLE mode, execution stops here:  The two sensors will trigger interupt 
		// and wake up the chip if necessary, or the RSSI module will do the same if this is a relaying node
        ENTER_IDLE_MODE();
	

		 // Enable high speed XOSC, switch clock source,turn on the ADC then disable 32kHz XOSC
        XOSC_ENABLE(TRUE);
        MAIN_CLOCK_SET_SOURCE(CLOCK_XOSC);
        X32_ENABLE(FALSE);
		
		halConfigADC(ADC_MODE_SINGLE | ADC_REFERENCE_INTERNAL_1_25, CC1010EB_CLKFREQ, 401);
    	ADC_SELECT_INPUT(ADC_INPUT_AD0);
		ADC_POWER(TRUE);

		
		Transmit();
        PrintTable();
        WaitRandom();
	    WaitRandom();
		Receive();
        WaitRandom();
	

		if(PORTBIT(2, 4) == TRUE){
		relay();
		}

		WaitRandom();

    }
} // main




//----------------------------------------------------------------------------
//  void WaitRandom (void)
//  
//  Description:
//      Wait for a random number of msecs (0 to 255*8=2040)
//      Note: The function uses busy waiting
//----------------------------------------------------------------------------
void WaitRandom (void) {
    byte xdata time;
    byte xdata n;

    time = rand();
    for (n = 0; n < waitMultiplier; n++) {
        halWait (time, CC1010EB_CLKFREQ);
    }
} // WaitRandom




//----------------------------------------------------------------------------
//  void Transmit (void)
//  
//  Description:
//      Update our temperature value (ADC) and transmit together with our node
//      ID and node name.
//----------------------------------------------------------------------------
void Transmit (void) {
    word xdata temp,temp2,soundlow,soundhigh, initialsound;
	word xdata test = 0xFF;
	word xdata test2= 0x00;
	int xdata n;

    // Indicate transmission
    RLED = LED_ON;
    YLED = LED_ON;
	
	
		



    // Power up the ADC and sample the temperature
   // ADC_SAMPLE_SINGLE();
     temp = halReadTempSensor();

    // Update the TX buffer and the table with the new temperature
    txDataBuffer[TEMP_OFFSET] = (temp >> 8) & 0xFF;
    txDataBuffer[TEMP_OFFSET + 1] = temp & 0xFF;
    nodeTemps[0] = temp;
    nodeLastT[0] = (int) sppGetTime();
    YLED = LED_OFF;

	//motion signal is on 3.2
	if(PORTBIT(3,2) == TRUE){
		txDataBuffer[MOTION_OFFSET] = (test >> 8) & 0xFF;
    	txDataBuffer[MOTION_OFFSET + 1] = test & 0xFF;
		
	
		
		}	

	ADC_SELECT_INPUT(ADC_INPUT_AD0);     //change to AD0-sound sensor
	
	ADC_SAMPLE_SINGLE();				// take a single sample	
    initialsound = ADC_GET_SAMPLE_10BIT();
	soundhigh=initialsound;                 //initialize sound high to the first reading	
	soundlow = initialsound;
	


	for(n=0;n<=10;n++)
	{
	
	ADC_SAMPLE_SINGLE();				// take a single sample	
    temp2 = ADC_GET_SAMPLE_10BIT();
	
	if (temp2 > soundhigh)
		soundhigh=temp2;	
	if (temp2 < soundlow)
		soundlow = temp2;
	
	}
	if((soundhigh - initialsound) > (initialsound - soundlow))
		temp2=soundhigh; 
	else
		temp2=soundlow;

	
		txDataBuffer[SOUND_OFFSET] = (temp2 >> 8) & 0xFF;
    	txDataBuffer[SOUND_OFFSET + 1] = temp2 & 0xFF;

	
	// Transmit the temperature

	for(n=0;n<=10;n++)
	{
	
	sppSend(&TXI);
    do { /*nothing*/ } while (sppStatus() != SPP_IDLE_MODE);
    RLED = LED_OFF;

}
	 


	} // Transmit





//----------------------------------------------------------------------------
//  void Receive (void)
//  
//  Description:
//      Receive a temperature broadcast packet and register it in the table
//      Temperatures older than 30 secs get thrown out
//----------------------------------------------------------------------------
void Receive (void) {
    byte xdata n,m,o;
    byte xdata nodeIndex;
    word xdata nodeID;
	//byte xdata b=1;
	//char relay[] = "relay";

    // Throw out "old" nodes (no updates during the last 30 seconds)
    for (n = 0; n < MAX_NODE_COUNT; n++) {
        if (((int) sppGetTime() - nodeLastT[n]) > 3000) {
            // Re-organize the list (by moving the remaining nodes up one index)
            for (m = n; m < (MAX_NODE_COUNT - 1); m++) {
                nodeIDs[m] = nodeIDs[m + 1];
                for (o = 0; o < NODE_NAME_LENGTH; o++) {
                    nodeNames[m][o] = nodeNames[m + 1][o];
                }
                nodeTemps[m] = nodeTemps[m + 1];
                nodeLastT[m] = nodeLastT[m + 1];
            }
        }
    }

    // Receive the packet (if any)
    YLED = LED_ON;
    sppReceive(&RXI);
    do { /*nothing*/ } while (sppStatus() != SPP_IDLE_MODE);
    YLED = LED_OFF;

   		
	// Process the packet
    if (RXI.status == SPP_RX_FINISHED) {
        GLED = LED_ON;

        // Get the node ID
        nodeID = (rxDataBuffer[0] << 8) + rxDataBuffer[1];

        // Get the node's index in the temperature table
        for (n = 0; n < MAX_NODE_COUNT; n++) {
            if (nodeIDs[n] == nodeID) {
                nodeIndex = n;
                break;
            } else if (nodeIDs[n] == UNUSED_NODE_ID) {
                nodeIndex = n;
                break;
            } /*else {
                nodeIndex = INVALID_NODE_INDEX;
            }*/
        }

        // Update the table
        if (nodeIndex != INVALID_NODE_INDEX) {
            nodeIDs[nodeIndex] = nodeID;
            for (n = 0; n < NODE_NAME_LENGTH; n++) {
                nodeNames[nodeIndex][n] = rxDataBuffer[n + NODE_ID_LENGTH];
            }
            nodeTemps[nodeIndex] = (rxDataBuffer[TEMP_OFFSET] << 8) + rxDataBuffer[TEMP_OFFSET + 1];
            nodeLastT[nodeIndex] = (int) sppGetTime();
        }
    } else {
        GLED = LED_OFF;
    }



} 




//----------------------------------------------------------------------------
//  void PrintTable (void)
//  
//  Description:
//      Executes keyboard commands (change of the waiting multiplier)
//      Prints the temperature table to the terminal window
//----------------------------------------------------------------------------
void PrintTable (void) {
    int xdata n,m;
    float xdata fTemp;
    word xdata timeDiff;

    // Receive key strokes
    if (RI_0) {
        RI_0 = 0;
        if (isdigit(UART0_RECEIVE())) {
            waitMultiplier = toint(UART0_RECEIVE());
        } else if (UART0_RECEIVE() == 'd') {
            for (n = 1; n < MAX_NODE_COUNT; n++) {
                nodeIDs[n] = UNUSED_NODE_ID;
            }
        }
        else if (UART0_RECEIVE() == 'n') {
            memset(&nodeNames[0][0],0,NODE_NAME_LENGTH);
            printf("\nEnter node name (use up to 20 characters):");
            scanf("%s", &nodeNames[0][0]);

            // Write new name into Flash
            halCopy2Flash(flashUnitName,&nodeNames[0][0],NODE_NAME_LENGTH,
                ramBufNonAligned, CC1010EB_CLKFREQ);

		    // Get our ID from CRC16(our name)
		    nodeIDs[0] = culFastCRC16Block(&nodeNames[0][0], NODE_NAME_LENGTH, CRC16_INIT);

		    // Prepare the id+name part of the packet
		    txDataBuffer[0] = (nodeIDs[0] >> 8) & 0xFF;
		    txDataBuffer[1] = nodeIDs[0] & 0xFF;
    
    		for (n = 0; n < NODE_NAME_LENGTH; n++) {
		        txDataBuffer[n + NODE_ID_LENGTH] = nodeNames[0][n];
		    }
        }
		 else if (UART0_RECEIVE() == 'b') 
		{
		printf("\nBrian is cooler than you");
		}
    }

    // Header:
    VT100_GO_TOP_LEFT();

    // Items:
    printf("TEMPERATURE LIST:\n");
    for (n = 0; n < MAX_NODE_COUNT; n++) {
        if (nodeIDs[n] == UNUSED_NODE_ID) {
            continue;
        }
        // Node number and ID:
        printf("\n%01X. (0x%X) ", n, nodeIDs[n]);

        // Node name:
        for (m = 0; m < NODE_NAME_LENGTH; m++) {
            UART0_WAIT_AND_SEND(nodeNames[n][m]);
        }

        // Temperature:
        fTemp = nodeTemps[n];
        fTemp -= 492;
        fTemp /= 8.192;
        printf(" - TEMP: %3.2f", fTemp);

		
		//putchar(fTemp);

        // Time after the last update
        timeDiff = abs((int) sppGetTime() - nodeLastT[n]) * 10;
        printf(" - AGE (msecs): %d", timeDiff);
        VT100_CLEAR_LINE_RIGHT();
    }

    // Available keyboard input options
    VT100_INSERT_BLANK_LINE();
    printf("\nPress '0'-'9' to change the waiting multiplier");
    VT100_CLEAR_LINE_RIGHT();
    printf("\nPress 'd' to empty the table");
    VT100_CLEAR_LINE_RIGHT();
    printf("\nPress 'n' to give the unit a new name");
    VT100_CLEAR_LINE_RIGHT();
	printf("\nPress 'b' if your cool");
    VT100_CLEAR_LINE_RIGHT();
    VT100_CLEAR_SCREEN_DOWN();

} // PrintTable


//----------------------------------------------------------------------------
//  void relay (void)
//  
//  Description:
//      transmit the recieved packet (relay it!).  
//----------------------------------------------------------------------------
void relay (void)
{
	
	char relay[] = "relay  ";  
	rxDataBuffer[0] = (relay[0] >> 8) & 0xFF;
 	rxDataBuffer[1] = relay[0] & 0xFF;
	
	
	sppSend(&TXIR);
	do { /*nothing*/ } while (sppStatus() != SPP_IDLE_MODE);

}

// Flash interrupt handler (do nothing)
// We need to handle the interrupt even though we do not do anything.
// If not, the program will not run correctly except under the debugger,
// which has its own Flash interrupt handler

void FlashIntrHandler(void) interrupt INUM_FLASH {
    
    INT_SETFLAG(INUM_FLASH, INT_CLR);    
    return;
}



// ISR (interrupt service routine) for DES and ADC, priority 9
// The interrupt must be cleared by software
void isr_des_adc() interrupt INUM_DES_ADC {
    INT_SETFLAG(INUM_DES_ADC, INT_CLR);
    if (INT_GETFLAG(INUM_ADC)) {
       
        INT_SETFLAG(INUM_ADC, INT_CLR);
       } 
} //end isr_des_adc()


// ISR (interrupt service routine) for ext int 0, priority 1
// The interrupt is cleared by hardware
void isr_extint0() interrupt INUM_EXTERNAL0 {
    //do nothing...just get out of idle mode
} 

// ISR (interrupt service routine) for ext int 1, priority 3
// The interrupt is cleared by hardware
void isr_extint1() interrupt INUM_EXTERNAL1 {
    //do nothing...just get out of idle mode
} 

