/*
 * multiplexer.cpp
 *
 * Created: 9.4.2016 15:58:50
 * Author : Ondrej Bostik
 */ 


#include "inc/definitions.h"
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include "inc/iomacros.h"
#include <stdlib.h>

#define INITshunt				0
#define INITinput				0
#define INITblock				1

#define INIT_REL_UHF_1			0
#define INIT_REL_UHF_2			0

#define RESET_RELAY_ON_STARTUP	1

/* UART related vars */
volatile char				UARTbuf[20];
volatile unsigned short		UARTpoz		= 0;
volatile unsigned short		UARTflag	= 0;

/* TIMER related vars */
volatile unsigned short		timerFLAG;
volatile unsigned short		timerEND;
volatile unsigned short		timerTIME;

/* status variables */
volatile unsigned short		ShuntSTAT;
volatile unsigned short		InputSTAT;
volatile unsigned short		BlockSTAT;

volatile unsigned short		ShuntSTATnew;
volatile unsigned short		InputSTATnew;
volatile unsigned short		BlockSTATnew;

/* other vars */
volatile unsigned short		int1FLAG;
volatile unsigned short		statChangeFLAG;

/* REL stat */
volatile unsigned short		rel_uhf_1;
volatile unsigned short		rel_uhf_2;

/* UART */
/*
    This part of code is based on tutorial from URL:
	https://hekilledmywire.wordpress.com/2011/01/05/using-the-usartserial-tutorial-part-2/
	This function were copied from tutorial article and modified for the purpose.
		ISR(USART_RX_vect)
		USART_Init()
		USART_putstring()
		USART_ReceiveByte()
*/

/* START OF THIRD PARTY CODE */

ISR(USART_RX_vect) {
	UARTbuf[UARTpoz] = UDR0;
	if (UARTbuf[UARTpoz] == '.')
	{
		UARTflag = 1;
		UARTbuf[UARTpoz] = '\0';
		UARTpoz = 0;
		sleep_disable();
	}
	UARTpoz++;
}

void USART_Init(void) {
	UBRR0L = BAUD_PRESCALE;
	UBRR0H = (BAUD_PRESCALE >> 8);

	// Enable receiver and transmitter and receive complete interrupt
	UCSR0B = ((1<<TXEN0)|(1<<RXEN0) | (1<<RXCIE0));

	UARTbuf[0] = '\0';
	UARTflag = 0;
}

void USART_SendByte(char u8Data){
	while((UCSR0A &(1<<UDRE0)) == 0);
	UDR0 = u8Data;
}

void USART_putstring(volatile char* StringPtr) {
	while(*StringPtr != 0x00){
		USART_SendByte(*StringPtr);
		StringPtr++;
	}
}

char USART_ReceiveByte(void) {
	while((UCSR0A &(1<<RXC0)) == 0);
	return UDR0;
}

/* END OF THIRD PARTY CODE */



void USART_ProcessCommand(void) {
	char  buffer[20];
	short err = 0, shunt = -1;
	switch (UARTbuf[0]) {
		case 'R': /* REPORT */
			USART_send("Input: ");
			itoa(InputSTAT+1,buffer,10);
			USART_putstring(buffer);
		
			USART_send(" \r\nShunt: ");
			itoa(ShuntSTAT+1,buffer,10);
			USART_putstring(buffer);
		
			USART_send(" \r\nBlock: ");
			if(BlockSTAT == 1)
				USART_send("On\r\n");
			else
				USART_send("Off\r\n");
			break;
		case 'I': /* INPUT */
			if (UARTbuf[1] == ' ') { /* I 1 */
				if (UARTbuf[2] == 'A') {
					InputSTATnew = 0;
				} else if (UARTbuf[2] == 'B') {
					InputSTATnew = 1;
				} else { err = 1; }
			} else if (UARTbuf[5] == ' ') { /* Input 1 */
				if (UARTbuf[6] == 'A') {
						InputSTATnew = 0;
					} else if (UARTbuf[6] == 'B') {
						InputSTATnew = 1;
					} else { err = 1; }
			} else { err = 1; }
			break;
		case 'B': /* Block */
			if (UARTbuf[1] == ' ') { /* B 1 */
				if (UARTbuf[2] == '0') {
					InputSTATnew = 0;
				} else if (UARTbuf[2] == '1') {
					InputSTATnew = 1;
				} else if (UARTbuf[3] == 'N') { //ON
					InputSTATnew = 1;
				} else if (UARTbuf[3] == 'F') { //OFF
					InputSTATnew = 0;
				} else { err = 1; }
			} else if (UARTbuf[5] == ' ') { /* Block 1 */
				if (UARTbuf[6] == '0') {
					InputSTATnew = 0;
				} else if (UARTbuf[6] == '1') {
					InputSTATnew = 1;
				} else if (UARTbuf[7] == 'N') { //ON
					InputSTATnew = 1;
				} else if (UARTbuf[7] == 'F') { //OFF
					InputSTATnew = 0;
				} else { err = 1; }
			} else { err = 1; }
			break;
		case 'S': /* Shunt */
			if (UARTbuf[1] == ' ') { /* S 1 */
					shunt = atoi((const char *) &UARTbuf[2]);		
				} else if (UARTbuf[5] == ' ') { /* Shunt 1 */
					shunt = atoi((const char *) &UARTbuf[6]);
				} else { err = 1; }
			
				if ((shunt > 0) && (shunt < 13)) {
					ShuntSTATnew = shunt - 1;
				} else {
					err = 1;
				}
			break;
		default:
			err = 1;
			break;
	}
	
	if (err == 1) {
		USART_send("Invalid command: ");
		USART_putstring(UARTbuf);
	} 
	else {
		USART_send("OK");
		statChangeFLAG = 1;
	}
	
	UARTflag = 0;
	UARTpoz = 0;
}

/* SPI */
void spi_init( void ) {
	DDR_SPI &= ~((1<<DD_MOSI)|(1<<DD_MISO)|(1<<DD_SS)|(1<<DD_SCK));
	DDR_SPI |= ((1<<DD_MOSI)|(1<<DD_SS)|(1<<DD_SCK));

	SPCR = ((1<<SPE)|(0<<SPIE)|(0<<DORD)|(1<<MSTR)|(0<<SPR1)|(1<<SPR0)|(0<<CPOL)|(0<<CPHA));
	SPSR = (1<<SPI2X);
}

char spi_one_byte (char data) {
	SPDR = data;
	while((SPSR & (1<<SPIF))==0);
	return SPDR;
}

void spi_array (uint8_t * data, uint8_t len) {
	for (uint8_t i = 0; i < len; i++) {
		SPDR = data[i];
		while((SPSR & (1<<SPIF))==0);
	}
}

void exp_send(char address, char val) {
	off(EXP_CS);
	spi_one_byte(0x40);    // write command (0b0100[A2][A1][A0][R/W]) also equal to 0x40
	spi_one_byte(address); // select IODIRB
	spi_one_byte(val);     // set direction
	on(EXP_CS);
}

/* Input relay drive */
void inputSend(uint8_t *buffer) {
	/* Relay driver for channel 1 - 8 */
	off(IN_CS);
	spi_array(buffer, 2);
	on(IN_CS);

	_delay_ms(20);
	off(IN_RST);
	_delay_ms(10);
	on(IN_RST);
}

void inputRst(void) {
	/* Reset channel 1 - 8 */
	_delay_ms(20);
	off(IN_RST);
	_delay_ms(10);
	on(IN_RST);
	
	uint8_t buffer[] = {0xAA, 0xAA};
	inputSend(buffer);
	
	/* Reset channel 9 - 12 */
	rel_uhf_1 = 0;
	rel_uhf_2 = 0;
	exp_send(GPIOA,  GPA(0x00) );
	_delay_ms(5);
	exp_send(GPIOA,  GPA(0x0A) );
	_delay_ms(20);
	exp_send(GPIOA,  GPA(0x00) );
	_delay_ms(10);
	
}

void inputOn(short i) {
	uint8_t buffer[] = {0x00, 0x00};
	
	inputRst();
	
	if ( i < 8 ) {
		/* Handle channel 1 - 8 */
		inputRst();
		buffer[1 - i/4] = (1 << ((2 * (i % 4))) );
		inputSend(buffer);
	} else if ( i == 8 ) {
		/* Handle channel 9 */
		exp_send(GPIOA,  GPA(0x01) );
		_delay_ms(20);
		exp_send(GPIOA,  GPA(0x00) );
		_delay_ms(10);
	} else if ( i == 9 ) {
		/* Handle channel 10 */
		exp_send(GPIOA,  GPA(0x04) );
		_delay_ms(20);
		exp_send(GPIOA,  GPA(0x00) );
		_delay_ms(10);
	} else if ( i == 10 ) {
		/* Handle channel 11 */
		rel_uhf_1 = 1;		
		exp_send(GPIOA,  GPA(0x00) );
	} else if ( i == 11 ) {
		/* Handle channel 12 */
		rel_uhf_2 = 1;
		exp_send(GPIOA,  GPA(0x00) );
	}
}

void inputOff(short i) {
	uint8_t buffer[] = {0x00, 0x00};

	if ( i < 8 ) {
		/* Handle channel 1 - 8 */
		inputRst();
		buffer[1 - i/4] = (1 << ((2 * (i % 4))+1) );
		inputSend(buffer);
	} else if ( i == 8 ) {
		/* Handle channel 9 */
		exp_send(GPIOA,  GPA(0x02) );
		_delay_ms(20);
		exp_send(GPIOA,  GPA(0x00) );
		_delay_ms(10);
	} else if ( i == 9 ) {
		/* Handle channel 10 */
		exp_send(GPIOA,  GPA(0x08) );
		_delay_ms(20);
		exp_send(GPIOA,  GPA(0x00) );
		_delay_ms(10);
	} else if ( i == 10 ) {
		/* Handle channel 11 */
		rel_uhf_1 = 0;
		exp_send(GPIOA,  GPA(0x00) );
	} else if ( i == 11 ) {
		/* Handle channel 12 */
		rel_uhf_2 = 0;
		exp_send(GPIOA,  GPA(0x00) );
	}
	
}


/* Output relay drive */
void outputSend(uint8_t *buffer) {
	off(OUT_CS);
	spi_array(buffer, 3);
	on(OUT_CS);

	_delay_ms(20);
	off(OUT_RST);
	_delay_ms(10);
	on(OUT_RST);
}

void outputRst(void) {
	_delay_ms(20);
	off(OUT_RST);
	_delay_ms(10);
	on(OUT_RST);
	
	uint8_t buffer[] = {0xAA, 0xAA, 0xAA};
	outputSend(buffer);
}

void outputOn(short i) {
	uint8_t buffer[] = {0x00, 0x00, 0x00};
	
	outputRst();
	buffer[2 - i/4] = (1 << ((2 * (i % 4))) );
	outputSend(buffer);
}

void outputOff(short i) {
	uint8_t buffer[] = {0x00, 0x00, 0x00};
	
	outputRst();
	buffer[2 - i/4] = (1 << ((2 * (i % 4))+1) );
	outputSend(buffer);
}

void statShow(void) {
	exp_send(GPIOB,  toogleLED(ShuntSTAT) | (InputSTAT << IN_LED) );
	exp_send(GPIOA,  GPA(0x00) );
}

void statHide(void) {
	exp_send(GPIOB, 0x00 | (InputSTAT << IN_LED));
}

void blinkOn(void) {
	//on(LED);
	if ((timerTIME % 2) == 0)
		exp_send(GPIOB, toogleLED(ShuntSTAT) | (InputSTATnew << IN_LED) );
	else
		exp_send(GPIOB, toogleLED(ShuntSTATnew) | (InputSTATnew << IN_LED) );
}

void blinkOff(void) {
	//off(LED);
	if (ShuntSTAT != ShuntSTATnew)
	{
		exp_send(GPIOB, toogleLED(ShuntSTAT) | (InputSTATnew << IN_LED) );
	} else {
		exp_send(GPIOB, (InputSTATnew << IN_LED) );
	}
}

/* Interrupts */
ISR (INT0_vect)
{
	_delay_ms(200);
	BlockSTATnew = get(BTN_BLOCK);
	statChangeFLAG = 1;
	sleep_disable();
}

ISR(INT1_vect)
{
	enableTimer0;
	int1FLAG = 1;
	sleep_disable();
}

ISR(TIMER0_COMPA_vect) {
	timerTIME++;
	timerFLAG = 1;
	if (timerTIME >= 3000) {	/* timeout 5s => 1000; 15s => 3000; 60s => 12000;  12000*/
		disableTimer0;
		timerEND = 1;
	}
	
}

/* GLOBAL PROG */
void setup (void) {
	/* Button setup */
	in(BTN_RIGHT);
	pullup(BTN_RIGHT);
	in(BTN_OK);
	pullup(BTN_OK);
	in(BTN_LEFT);
	pullup(BTN_LEFT);

	/* RST setup */
	out(IN_RST);
	off(IN_RST);
	out(OUT_RST);
	off(OUT_RST);
	out(EXP_RST);
	off(EXP_RST);
	_delay_ms(50);
	on(IN_RST);
	on(OUT_RST);
	on(EXP_RST);

	/* CS setup */
	out(IN_CS);
	on(IN_CS);
	out(OUT_CS);
	on(OUT_CS);
	out(EXP_CS);
	on(EXP_CS);

	/* SPI setup */
	spi_init();

	/* Expander setup */
	exp_send(IODIRA, 0x00);
	exp_send(IODIRB, 0x00);
	
	/* STATUS VARS init */
	ShuntSTAT = INITshunt;
	InputSTAT = INITinput;
	BlockSTAT = INITblock;
	
	ShuntSTATnew = INITshunt;
	InputSTATnew = INITinput;
	BlockSTATnew = INITblock;
	
	rel_uhf_1 = INIT_REL_UHF_1;
	rel_uhf_1 = INIT_REL_UHF_2;
	statShow();	
	
	/* UART setup */
	USART_Init();
	
	/* EXT Interrupt setup */
	EICRA = 0<<ISC01 | 1<<ISC00 | 1<<ISC11 | 0<<ISC10;
	enableINT0;
	enableINT1;
	
	/* FLAGS setup */
	timerFLAG = 0;
	timerTIME = 0;
	timerEND = 0;
	
	/* Power down & noise down */
	PRR = (
		(1 << PRTWI) |				// Power Reduction TWI
		(1 << PRTIM2) |				// Power Reduction Timer/Counter2
		(1 << PRTIM1) |				// Power Reduction Timer/Counter1
		(1 << PRADC)				// Power Reduction ADC
	);
	
	/* TIMER setup */
    TCCR0A |= (1 << WGM01);			// Set the Timer Mode to CTC
	OCR0A = 0x13;					// 0x13 = 5ms, 0x60 = 25ms
	TIMSK0 |= (1 << OCIE0A);		// Set the ISR COMPA vect
	
	if ( RESET_RELAY_ON_STARTUP == 1) {
		outputRst();
		inputRst();
		
		outputOn(ShuntSTAT);
		inputOn(ShuntSTAT);
	}

	set_sleep_mode(SLEEP_MODE_IDLE);
	cli();
	sei();
	
	sleep_enable();
	sleep_cpu();
	sleep_disable();

}

int main (void) {
	unsigned int oldTIME = 0;
	char ShiftRegR = 0xff;
	char ShiftRegL = 0xff;
	char ShiftRegI = 0xff;
	char ShiftRegOK = 0x00;
	unsigned short PressedR = 0;
	unsigned short PressedL = 0;
	unsigned short InPress = 0;
	unsigned short PressedOK = 1;
	
	setup();
	
	while (1)
	{
		if ( int1FLAG == 1 ) {
			/* blink LED */
			if ((timerTIME % 200) <= 100) {
				blinkOn();
			} else {
				blinkOff();
			}
			
			/* Buttons */
			if (oldTIME != timerTIME)
			{
				oldTIME = timerTIME;
				
				
				//BUTTON INPUT
				ShiftRegI = ((ShiftRegI << 1) | get(BTN_IN));
				if ((InPress == 1) && (ShiftRegI == 0xff)) {	//RELEASE, FALLING EDGE
					InPress = 0;
				}
				if ((InPress == 0) && (ShiftRegI == 0x00)) {	//HOLD, RISING EDGE
					InPress = 1;
					timerTIME = 0;
					InputSTATnew = 1 - InputSTATnew;
				}
				
				
				//BUTTON RIGHT
				ShiftRegR = ((ShiftRegR << 1) | get(BTN_RIGHT));
				if ((PressedR == 1) && (ShiftRegR == 0xff)) {	//RELEASE, FALLING EDGE
					PressedR = 0;
				}
				if ((PressedR == 0) && (ShiftRegR == 0x00)) {	//HOLD, RISING EDGE
					PressedR = 1;
					timerTIME = 0;
					ShuntSTATnew++;
					if (ShuntSTATnew > 11)
					ShuntSTATnew = 0;
				}
								
				//BUTTON LEFT
				ShiftRegL = ((ShiftRegL << 1) | get(BTN_LEFT));
				if ((PressedL == 1) && (ShiftRegL == 0xff)) {	//RELEASE, FALLING EDGE
					PressedL = 0;
				}
				if ((PressedL == 0) && (ShiftRegL == 0x00)) {	//HOLD, RISING EDGE
					PressedL = 1;
					timerTIME = 0;
					if (ShuntSTATnew == 0) {
						ShuntSTATnew = 11;
					}
					else {
						ShuntSTATnew--;
					}
				}
				
				//BUTTON OK
				ShiftRegOK = ((ShiftRegOK << 1) | get(BTN_OK));
				if ((PressedOK == 1) && (ShiftRegOK == 0xff)) {	//RELEASE, FALLING EDGE
					PressedOK = 0;
				}
				if ((PressedOK == 0) && (ShiftRegOK == 0x00)) {	//HOLD, RISING EDGE
					PressedOK = 1;
					timerTIME = 0;
					timerEND = 1;
					
					statChangeFLAG = 1;
				}			
			} /*buttons - clock tick */
		} /*end INT1 flag */
		
		
		/* USART handle routine */
		if (UARTflag == 1) {
			USART_ProcessCommand();
		}/*end USARTflag */
		
		/* Change status routine */
		if (statChangeFLAG == 1) {
			//If shunt change request was send
			if(ShuntSTAT != ShuntSTATnew) {
				//Validation
				if ((ShuntSTATnew >=0) && (ShuntSTATnew < 12))
				{
					ShuntSTAT = ShuntSTATnew;
					outputOn(ShuntSTAT);
					inputOn(ShuntSTAT);
				} else {
					ShuntSTATnew = ShuntSTAT;
				}
			}
			
			//If shunt change request was send
			if(InputSTAT != InputSTATnew) {
				//Validation
				if ((InputSTATnew >=0) && (InputSTATnew < 2))
				{
					InputSTAT = InputSTATnew;
				} else {
					InputSTATnew = InputSTAT;
				}
			}
			
			//If shunt change request was send
			if(BlockSTAT != BlockSTATnew) {
				//Validation
				if ((BlockSTATnew >=0) && (BlockSTATnew < 2))
				{
					BlockSTAT = BlockSTATnew;
				} else {
					BlockSTATnew = BlockSTAT;
				}
			}
			
			//Clear statChangeFLAG
			statChangeFLAG = 0;
			statShow();
		} /* END statChangeFLAG routine */
		
		if (timerEND != 0) {
			disableTimer0;
			statChangeFLAG = 1;
			timerEND = 0;
			timerFLAG = 0;
			timerTIME = 0;
			int1FLAG = 0;
			ShiftRegR = 0xff; PressedR = 0;
			ShiftRegL = 0xff; PressedL = 0;
			ShiftRegI = 0xff; InPress = 0;
			ShiftRegOK = 0x00; PressedOK = 1;
			blinkOff();
			statShow();
			
			sleep_enable();
			sleep_cpu();
			sleep_disable();
		} /*end timerEND flag routine */
		
	} /*END endless WHILE */
		
}/* END MAIN */