#include "msp.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>


// ==========Definizione costanti==========

typedef enum {false=0, true=1} bool;      // definizione tipo bool

// costanti dei tempi di acquisizione
#define STEP			10		//	10 us	 tempo dell'interrupt
#define TIME_ON			50		//	200 us	 led IR on, signal = 0 V
#define TIME_ADC_START	0		//	100 us	 inizio istante di acquisizione
#define TIME_OFF		450		//	650 us	 led IR off, signal = 3,3 V
#define TIME_ADC_END	1000	//	800 us	 fine istante di acquisizione
#define TIME_SAVE		5000	//	900 us	 controllo fine ciclo
#define TIME_RESET		10000	//	1.000 us periodo del segnale generato

// costanti comandi protocollo trasmissione seriale
#define com_PING  'P'         // ping
#define com_T     'T'         // parametro T (periodo in minuti)
#define com_N     'N'         // parametro N (numero campioni per periodo)
#define com_UTC   'U'         // sincronizzazione data/ora
#define com_MODE  'A'         // 0 (off) - 1 (auto) - 2 (manual)
#define com_START 'S'         // inizio acquisizione degli N valori
#define com_RESET 'R'         // provoca reset

#define com_VALUE 'D'         // invio di N valori
#define com_END   'E'         // fine invio valori
#define com_LOG   'L'         // stringa di log


// ==========Dichiarazione variabili globali==========

// parametri modificabili via seriale a run-time
int T =   5;  // min      // Periodo globale di acquisizione               [N/100/60  < T  < 32767]
int N = 100;              // Numero di campioni memorizzati per periodo    [    1     < N  < 16384]

// variabili tempi
int microsecondi = 0;      // contatore di microsecondi del timer A0        [0 - 10.000]
int minuti = 0;            // contatore di minuti del Real Time Clock       [0 - T]

// indici contatori array
int index_N = 0;           // contatore campioni                            [0 - N]
int index_Singola = 0;

// variabili acquisizione
uint16_t values[16384];    // array che contiene tutti gli N valori di un periodo
uint16_t values_Singola[50];

char micro[8];

// variabili ricezione seriale
char RXData[32];           // buffer che contiene la stringa ricevuta
char RXByte;               // ultimo byte ricevuto
int  index_RX;             // indice del ciclo che riempie il buffer RXData
int indice;
int somma = 0;
int media = 0;



// ==========Funzioni in/out==========

void IR_off()    {P4OUT    |=  BIT2;}
void IR_on()     {P4OUT    &= ~BIT2;}
//void fan_on()  {P4OUT    |=  BIT4;}
//void fan_off() {P4OUT    &= ~BIT4;}
void led_on()    {P1OUT    |=  BIT0;}
void led_off()   {P1OUT    &= ~BIT0;}
void TA0_on()    {TA0CCTL0 |=  CCIE;}
void TA0_off()   {TA0CCTL0 &= ~CCIE;}
void auto_on(){
	//fan_on();
	RTCCTL0_H = RTCKEY_H;
	RTCCTL0_L |= RTCTEVIE;
	RTCCTL0_H = 0;}
void auto_off(){
	//fan_off();
	RTCCTL0_H = RTCKEY_H;
	RTCCTL0_L &= ~RTCTEVIE;
	RTCCTL0_H = 0;}

// ===========================Funzioni Real Time Clock===========================

void get_now(char *now){
	// legge i registri del 'Real Time Clock' e scrive la data in now
	sprintf(now     , "%04x-",  RTCYEAR);
	sprintf(now +  5, "%02x-",  RTCMON);
	sprintf(now +  8, "%02x ",  RTCDAY);
	sprintf(now + 11, "%02x:",  RTCHOUR);
	sprintf(now + 14, "%02x:",  RTCMIN);
	sprintf(now + 17, "%02x\n", RTCSEC);
}

void set_rtc(char *date){
	// riceve una data in formato stringa 'YYYY-MM-DD hh:mm:ss' e scrive i registri del 'Real Time Clock'
	RTCCTL0_H = RTCKEY_H;                       //   Unlock RTC key protected registers
	RTCYEAR = strtol(&date[ 0], NULL, 16);
	RTCMON  = strtol(&date[ 5], NULL, 16);
	RTCDAY  = strtol(&date[ 8], NULL, 16);
	RTCHOUR = strtol(&date[11], NULL, 16);
	RTCMIN  = strtol(&date[14], NULL, 16);
	RTCSEC  = strtol(&date[17], NULL, 16);
    RTCCTL0_H = 0;                              //   Lock RTC key protected registers
}



// ===========================Funzioni Protocollo Seriale===========================

void send_char(char c){                       // invia un carattere sulla seriale
	while(!(UCA0IFG&UCTXIFG)); //finchè c' da trasmettere
	UCA0TXBUF = c; //riempie il buffer di trasmissione
}

int send(char command, char *text){
	send_char(command);                       // invio comando
	int i=0;
	while (text[i] != 10){				      // finchè è diverso da 'a capo \n'
		send_char(text[i]);
		i++;
	}                                         // invio testo
	send_char(10);                            // invio '\n'
	return i+2;                               // restituisce il numero di caratteri inviati
}

void send_all_values(){
	int i, i_N;
	int n=0;
	int samples_per_row = 20; //sono il numero di campioni su gni riga stampati su monitor (10000/20=500 righe di campioni adc)
	char TXData[32];
	char UTC[32];
	get_now(UTC);
	for (i_N=0; i_N<N; i_N++){
		if (i_N == n){
			n += samples_per_row;
			send_char(com_VALUE);
			for(i = 0; i < 19; i++) //19 lunghezza timestamp
				send_char(UTC[i]);
			send_char(';');
		}
		sprintf(TXData, "%d,", values[i_N]);
		for(i = 0; i < strlen(TXData); i++)
			send_char(TXData[i]);
		if (i_N == n-1 || i_N == N-1){
			send_char(10);
		}
	}
	send(com_END, "E\n");
}


void received(){                              // elabora i dati ricevuti dalla seriale
	char TXData[32];
	int mode;
	switch(RXData[0]){
		case (int)com_PING:
			send(com_PING, "ok\n");
			break;
		case (int)com_T:
			T = atoi(&RXData[1]);
			sprintf(TXData, "%d\n", T);
			send(com_T, TXData);
			break;
		case (int)com_N:
			N = atoi(&RXData[1]);
			sprintf(TXData, "%d\n", N);
			send(com_N, TXData);
			break;
		case (int)com_UTC:
			set_rtc(&RXData[1]);
			get_now(TXData);
			send(com_UTC, TXData);
			break;
		case (int)com_MODE:
			mode = atoi(&RXData[1]);
			sprintf(TXData, "%d\n", mode);
			send(com_MODE, TXData);
			if (mode == 1) {
				auto_on();
			}
			else {
				auto_off();
			}
			break;
		case (int)com_START:
			microsecondi = 0;
			TA0_on();
			led_on();
			send(com_START, "\n");
			break;
		case (int)com_RESET:
			send(com_LOG, "ricevuto comando reset\n");
			RSTCTL_RESET_REQ |= (0x6900 | RSTCTL_RESET_REQ_HARD_REQ);
		default:
			send(com_LOG, "comando sconosciuto\n");
			break;
	}
}

// ===========================Funzioni adc===========================
int main(void)
{
	// ==========Configurazioni di base==========
	WDTCTL = WDTPW | WDTHOLD; // Stop watchdog timer

	P1DIR |= BIT0;
	P1OUT |= BIT0;                     // Set 1.0 as digital out (led rosso a bordo)

	P4DIR |= BIT2;
	P4OUT |= BIT2;              // Set 4.2 as digital out (IR)

	SCB_SCR |= SCB_SCR_SLEEPONEXIT;    // Enable sleep on exit from ISR
	__enable_interrupt();


	// ==========Configurazione ADC 14 bit==========
	P6SEL1 |= BIT1;
	P6SEL0 |= BIT1;    // Canale A14 (P6.1) configurato per ADC

	ADC14CTL0 &= ~ADC14ENC;							  //Turn off Enable. With few exceptions, the ADC14 control bits can only be modified when ADC14ENC = 0.
	ADC14CTL0 = ADC14ON | ADC14SHP | ADC14CONSEQ_2 | ADC14SHS_0;   // Turn on ADC14, set sampling time, repeat single channel
	ADC14CTL1 = ADC14RES__14BIT;                      // Use sampling timer, 14-bit conversion results
	ADC14MCTL0 = ADC14VRSEL_0 | ADC14INCH_14;         // A0 ADC input select; Vref=AVCC=3.3V
	//ADC14CLRIFGR0 |= ADC14IFG0;
	ADC14IER0 |= ADC14IE0;
	ADC14CTL0 |= ADC14ENC;                          // ADC enable


	// ==========Configurazione clock a 12 MHz==========
	CSKEY = 0x695A;                    // Unlock CS module for register access
	CSCTL0 = 0;                        // Reset tuning parameters
	CSCTL0 = DCORSEL_3;                // Set DCO to 12MHz (nominal, center of 8-16MHz range)
	CSCTL1 = SELA_2 | SELS_3 | SELM_3; // Select ACLK = REFO, SMCLK = MCLK = DCO
	CSKEY = 0;                         // Lock CS module from unintended accesses


	// ==========Configurazione TimerA0==========
	NVIC_ISER0 = 1 << ((INT_TA0_0 - 16) & 31);  // Enable TA0_0 interrupt in NVIC module
	TA0CCTL0 &= ~CCIFG;                         // Interrupt disable
	TA0CCR0 = STEP-1;                             // Overflow (step dell'interrupt)
	TA0CTL = TASSEL__SMCLK | MC__UP | ID_2;     // SMCLK, UP mode, Divisione per 4 (12 MHz / 4 = 3 MHz)
	TA0EX0 |= TAIDEX_2;                         // Divisione per 3 (3 MHz / 3 = 1 MHz)


	// ==========Configurazione Seriale==========
	P1SEL0 |= BIT2 | BIT3;                       // set 2-UART pin as second function
	NVIC_ISER0 = 1 << ((INT_EUSCIA0 - 16) & 31); // Enable eUSCIA0 interrupt in NVIC module

	// Configure UART
	UCA0CTLW0 |= UCSWRST;
	UCA0CTLW0 |= UCSSEL__SMCLK;                  // Put eUSCI in reset
	    /* Baud Rate calculation
	     * 12.000.000/(16*9.600) = 78.125
	     * Fractional portion = 0.125
	     * User's Guide Table 21-4: UCBRSx = 0x10
	     * UCBRFx = int ( (78.125-78)*16) = 2
	     */
	UCA0BR0 = 78;                                 // 12.000.000 / 16 / 9.600 = 78,125
	UCA0BR1 = 0x00;
	UCA0MCTLW = 0x1000 | UCOS16 | 0x0020;
	UCA0CTLW0 &= ~UCSWRST;                        // Initialize eUSCI
	UCA0IE |= UCRXIE;                             // Enable USCI_A0 RX interrupt


	// ==========Configurazione Real Time Clock==========
    RTCCTL0_H = RTCKEY_H;                    // Unlock RTC key protected registers
    RTCCTL0_L &= ~RTCTEVIE;                  // Interrupt disable
    RTCCTL0_L &= ~(RTCTEVIFG);
    RTCCTL1 = RTCBCD | RTCHOLD;              // RTCTEVx -> 00b = Minute changed event
    // RTC enable, BCD mode, RTC hold
    // enable RTC read ready interrupt
    // enable RTC time event interrupt
    RTCCTL1 &= ~(RTCHOLD);                    // Start RTC calendar mode
    RTCCTL0_H = 0;                            // Lock the RTC registers

    NVIC_ISER0 = 1 << ((INT_RTC_C - 16) & 31);
    NVIC_ISER0 = 1 << ((INT_ADC14 - 16) & 31);

	// ==========Inizializzazione==========
    //fan_off();
    led_off();							// Led msp spento (si accende solo alle acquisizioni)
    IR_off();							// Led IR spento
    set_rtc("2000-01-01 00:00:00");		// Setto rtc

	__sleep();
	//while(1);
}


// ===========================Interrupt Service Routines===========================
void ADC14_IRQHandler(void) {
	values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
	index_N++;
}

// Timer A0 interrupt service routine
void TimerA0_0IsrHandler(void) {
    TA0CCTL0 &= ~CCIFG;
    /* STEP = 20; Tempo di campionamento compreso tra adc_start, adc_end; Riempio array e invio su seriale */
	if(microsecondi > TIME_ADC_START && microsecondi <= TIME_ADC_END) {
		ADC14CTL0 |= ADC14SC; // Start sampling/conversion; Enable and start conversion.
		//sprintf(micro, "%07d\n", microsecondi);	// Converto da intero a stringa dentro variabile micro
		//send(com_LOG, micro);						// ---------->USED TO CONTROL TIMERA AND FREQUENCY
		//while(!(ADC14IFGR0 & BIT0));				// Controlla che non abbia finito
		//values[index_N] = ADC14MEM0;				// Scrive valore buffer nell'array
		//index_N++;									// Incrementa indice array
	}
    switch (microsecondi){
		case TIME_ON:	// Accendo IR
			IR_on();
			//send(com_LOG, "IR_ON\n");
			break;
		case TIME_OFF:	// Spengo IR
			IR_off();
			//send(com_LOG, "IR_OFF\n");
			break;
		case TIME_SAVE:	// Invio valori
			/*
			QUI DENTRO FACCIO IL CALCOLO MEDIA DELL'ARRAY2 per metterlo in quello principale
			ovvero la values[index_N] = "media calcolata da values[index_M]"
			*/
				/*for(indice = 0; indice < index_Singola; indice ++) {
					somma += values_Singola[indice];
				}
				media = somma/index_Singola;
				values[index_N] = media;
				index_N++;
				media = 0;
				somma = 0;
				index_Singola = 0;*/
				if (index_N >= N){
					index_N = 0;
					microsecondi = 0;
					TA0_off();
					led_off();
					send_all_values();
				}
		default:
			break;
		}
    microsecondi += STEP;	// Interrupt ogni STEP
    if (microsecondi >= TIME_RESET){
    	microsecondi = 0;
    }
}


// UART interrupt service routine (Seriale)
void eUSCIA0IsrHandler(void) {
	/* Eseguita quando viene ricevuto un byte sulla seriale.
	 * Se il byte e' 10 (\n) chiama received che elabora il messaggio ricevuto
	 */
    if (UCA0IFG & UCRXIFG) {
		while(!(UCA0IFG & UCTXIFG));
		RXByte = UCA0RXBUF;
		RXData[index_RX++] = RXByte;
		if(RXByte==10) {
		  index_RX=0;
		  received();
		}
    }
}

// RTC interrupt service routine (essendo in real time VA IN MINUTI vedi configurazione SOPRA rtc)
void RtcIsrHandler(void) {
    if (RTCCTL0 & RTCTEVIFG) {
    	// Evento abilitato in modalita' automatica, accade ogni minuto
    	if (minuti == 0){
    		TA0_on();
			led_on();
    	}
		// Se T e' maggiore di 5 minuti, spengo la ventola e la riaccendo un minuto prima di ricominciare
		/*if (T > 5){
			if (minuti == 4){
				//fan_off();
			}
			if (minuti >= T-1){
				//fan_on();
			}
		}*/
		minuti++;
		if (minuti >= T) {
			minuti = 0;
			microsecondi = 0;
		}
	}
    // Chiudo l'interrupt flag
	RTCCTL0_H = RTCKEY_H;
	RTCCTL0_L &= ~RTCTEVIFG;
	RTCCTL0_H = 0;
}
