/*
 *  Author: Herudek
 */ 
/*


#define F_CPU	1000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <avr/pgmspace.h>


#define RELAY_DELAY_COUNTER			250 //240, 1024 PRSC, 1 MHz = 250 ms
#define RELAY_DELAY_COUNTER_VALUE	(256-RELAY_DELAY_COUNTER)

#define KEY0	'0'
#define KEY1	'1'
#define KEY2	'2'
#define KEY3	'3'
#define KEY4	'4'
#define KEY5	'5'
#define BARRIER	'6'
#define NO_KEY	'7'

//relay behavior modes
#define ON_STOP_OFF	1//the first push of the TX button activates the relay, the second push deactivates the relay
#define ON_HOLD_OFF	2//relay is active until button is released, if TX signal is lost, relay stays active until any button is pressed
#define ON_WAIT_OFF 3//the first push of the TX button activates the relay, after specific time the relay deactivates, another push means immediate relay deacivation

#define RELAY_ACTIVE_ON_WAIT_OFF_MODE_DEACTIVATE_NEXT_TIME	3
#define RELAY_ACTIVE_ON_WAIT_OFF_MODE	2
#define RELAY_ACTIVE_ON_STOP_OFF_MODE	1
#define NOTHING							0

#define RELAY0_ADC_channel		0
#define RELAY1_ADC_channel		1
#define RELAY2_ADC_channel		2
#define RELAY3_ADC_channel		3
#define RELAY4_ADC_channel		4
#define RELAY5_ADC_channel		5

#define RELAY0					0
#define RELAY1					1
#define RELAY2					2
#define RELAY3					3
#define RELAY4					4
#define RELAY5					5

#define NUMBER_OF_RELAYS		6

#define EEPROM_SIZE_USED		256 //in bytes
#define EEPROM_INFO_BYTE		0x1FF //for check if EEPROM is formatted
#define EEPROM_RELAYS_ON		0x1FE //remember which relays are turned on, so if power is lost, they can turn on on start

#define TIMER1_MAX				65536

#define Relay0_on()		PORTB |= 0b00000001
#define Relay0_off()	PORTB &= 0b11111110
#define Relay0_toggle()	PORTB ^= 0b00000001
#define Relay0_read()	(PINB &  0b00000001)

#define Relay1_on()		PORTB |= 0b00000010
#define Relay1_off()	PORTB &= 0b11111101
#define Relay1_toggle()	PORTB ^= 0b00000010
#define Relay1_read()	(PINB &  0b00000010)

#define Relay2_on()		PORTB |= 0b00000100
#define Relay2_off()	PORTB &= 0b11111011
#define Relay2_toggle()	PORTB ^= 0b00000100
#define Relay2_read()	(PINB &  0b00000100)

#define Relay3_on()		PORTB |= 0b00001000
#define Relay3_off()	PORTB &= 0b11110111
#define Relay3_toggle()	PORTB ^= 0b00001000
#define Relay3_read()	(PINB &  0b00001000)

#define Relay4_on()		PORTB |= 0b00010000
#define Relay4_off()	PORTB &= 0b11101111
#define Relay4_toggle()	PORTB ^= 0b00010000
#define Relay4_read()	(PINB &  0b00010000)

#define Relay5_on()		PORTB |= 0b00100000
#define Relay5_off()	PORTB &= 0b11011111
#define Relay5_toggle()	PORTB ^= 0b00100000
#define Relay5_read()	(PINB &  0b00100000)

#define LED_on()		PORTD |= 0b10000000
#define LED_off()		PORTD &= 0b01111111
#define LED_toggle()	PORTD ^= 0b10000000

#define Button_read()	(PIND & 0b01000000)//negative logic
#define Jumper_read()	(PINB & 0b01000000)//negative logic

#define RXpin_read()	(((~PIND)>>2) & 0b00000001)//negative logic
#define RXpin_read_value()	(PIND & 0b00000100)//negative logic

#define TIMER1_START()	TCCR1B = 0b00000001//timer 1 run
#define TIMER1_STOP()	TCCR1B = 0b00000000//timer 1 stop

#define RELAY_TIMER_PAUSE()		TCCR0 = 0b00000000;//pause relay delay timer 0 - prescaler to 0 = stop
#define RELAY_TIMER_UNPAUSE()	TCCR0 = 0b00000101;//continue relay delay timer 0 - prescaler to CLK/1024

#define DISABLE_INT0()		GICR = 0b00000000//disable INT0
#define ENABLE_INT0()		GICR = 0b01000000//enable INT0
#define CLEAR_INT0_FLAG()	GIFR = 0b01000000//clear INT0 flag otherwise INT0 ISR will trigger immediately

static volatile unsigned char RCdata[EEPROM_SIZE_USED];
static volatile unsigned char receivedID=0, receivedKey=0, newDataReceived=0, makeSample=0;

static volatile unsigned char relayMode[NUMBER_OF_RELAYS], relayInfo[NUMBER_OF_RELAYS], relayDelay[NUMBER_OF_RELAYS], relayNoKeyTimeout[NUMBER_OF_RELAYS];


void StartRelayDelaytimer()//timer 0
{
	TCCR0 = 0b00000101;//prescaler to CLK/1024
	TIMSK |= 0b00000001;//enable timer 0 OVF interrupt
	TCNT0 = RELAY_DELAY_COUNTER_VALUE;
}

void StopRelayDelaytimer()//timer 0
{
	TCCR0 = 0b00000000;//prescaler to 0 = stop
}

void ContinueRelayDelaytimer()//timer 0
{
	TCCR0 = 0b00000101;//prescaler to CLK/1024
}

void PauseRelayDelaytimer()//timer 0
{
	TCCR0 = 0b00000000;//prescaler to 0 = stop
}

ISR(TIMER1_OVF_vect)//generates sampling time
{
	makeSample = 1;
}

ISR(INT0_vect, ISR_NOBLOCK)//external interrupt INT0
{
	//start timer 1 for measuring length of the signal period
	TCNT1 = 0;
	TIMER1_START();
	DISABLE_INT0();//disable INT0	
	RELAY_TIMER_PAUSE();//pause relay delay timer 0 - prescaler to 0 = stop	
	
	unsigned char i=0, j=0;
	unsigned int timeMeasured=0;
	unsigned char dataReceivedString[20];	
	makeSample=0;
	
	while(!RXpin_read_value())//measuring "1" period
	{
		if(j++>5000)//period is too long
		{
			TIMER1_STOP();
			RELAY_TIMER_UNPAUSE();//continue relay delay timer 0 - prescaler to CLK/1024
			CLEAR_INT0_FLAG();//clear INT0 flag otherwise INT0 ISR will trigger immediately
			ENABLE_INT0();//enable INT0
			return;
		}
	}

	j=0;
	while(RXpin_read_value())//measuring "0" period
	{
		if(j++>5000)//period is too long
		{
			TIMER1_STOP();
			RELAY_TIMER_UNPAUSE();//continue relay delay timer 0 - prescaler to CLK/1024
			CLEAR_INT0_FLAG();//clear INT0 flag otherwise INT0 ISR will trigger immediately
			ENABLE_INT0();//enable INT0
			return;
		}
	}

	//stop timer and read the time
	TIMER1_STOP();

	timeMeasured = TCNT1/2-35;//measured time is in microseconds since CLK is 1 MHz
	if(timeMeasured>5000 || timeMeasured<3000)//period is too long or too short (3 to 5 ms per period tolerance)
	{
		Relay2_on();
		TIMER1_STOP();
		RELAY_TIMER_UNPAUSE();//continue relay delay timer 0 - prescaler to CLK/1024
		CLEAR_INT0_FLAG();//clear INT0 flag otherwise INT0 ISR will trigger immediately
		ENABLE_INT0();//enable INT0
		return;
	}
	
	//data packet is 20 bits long, takes 20*4=80 ms of time, 150 ms is minimal gap between bursts
	
	TCNT1 = TIMER1_MAX-(timeMeasured/2);//half of the period to ensure reading in the middle of the period
	TIMER1_START();
	
	while(makeSample==0);//sampling time generated by timer 1
	makeSample=0;

	//should receive: 0b 001X XXX0 1XXX X01X XX01;//0x20821	
	for(i=0;i<18;i++)//receiving the data, timing is important! - whole cycle should take exactly 4 ms
	{
		dataReceivedString[i] = RXpin_read();
		LED_toggle();
		TCNT1 = TIMER1_MAX-timeMeasured;//set delay time for timer 1
		while(makeSample==0);//sampling time generated by timer 1
		makeSample=0;
	}
	TIMER1_STOP();
	LED_off();

	receivedID = 0;
	receivedKey = 0;
			
	if(dataReceivedString[0 ] == 1 && dataReceivedString[ 5] == 0 && dataReceivedString[ 6] == 1 && dataReceivedString[11] == 0 && 
	   dataReceivedString[12] == 1 && dataReceivedString[16] == 0 && dataReceivedString[17] == 1)
	{
		receivedID |= (dataReceivedString[ 1]<<(7));
		receivedID |= (dataReceivedString[ 2]<<(6));
		receivedID |= (dataReceivedString[ 3]<<(5));
		receivedID |= (dataReceivedString[ 4]<<(4));
		receivedID |= (dataReceivedString[ 7]<<(3));
		receivedID |= (dataReceivedString[ 8]<<(2));
		receivedID |= (dataReceivedString[ 9]<<(1));
		receivedID |= (dataReceivedString[10]);
				
		receivedKey |= (dataReceivedString[13]<<(2));
		receivedKey |= (dataReceivedString[14]<<(1));
		receivedKey |= (dataReceivedString[15]);
		receivedKey += '0';				
		LED_on();
	}
	
		
	_delay_ms(20);//wait some to ensure not to read for forbidden period of time
	newDataReceived = 1;//ensures data will be processed in the main loop
	LED_off();

	TIMER1_STOP();//make sure timer 1 is not running
	TCNT1=0;
	RELAY_TIMER_UNPAUSE();//continue relay delay timer 0 - prescaler to CLK/1024
	CLEAR_INT0_FLAG();//clear INT0 flag otherwise INT0 ISR will trigger immediately
	ENABLE_INT0();//enable INT0
}

ISR(TIMER0_OVF_vect, ISR_NOBLOCK)//every approx. 250 ms this is called to subtract the time when relays are on
{
	unsigned char relayCount;
	unsigned int relayDelayCount=0;
	
	for(relayCount=0; relayCount < NUMBER_OF_RELAYS; relayCount++)
	{
		if(relayDelay[relayCount] > 0)
		{
			relayDelay[relayCount]--;
		}
		relayDelayCount += relayDelay[relayCount];
	}
		
	if(relayDelayCount > 0)
	{
		LED_toggle();
	}
	else if(Button_read() != 0)
	{
		LED_off();
	}
	
	TCNT0 = RELAY_DELAY_COUNTER_VALUE;//244, 1024 PRSC, 1 MHz = 250 ms
}

unsigned char EEPROM_read(unsigned int address)
{
	while(EECR & (1<<EEWE));//Wait for completion of previous write
	
	EEAR = address;//Set up address register
	EECR |= (1<<EERE);//Start EEPROM read by writing EERE
	
	return EEDR;//Return data from data register
}

void EEPROM_write(unsigned int address, unsigned char data)
{
	if(data == EEPROM_read(address))
	{
		return;
	}
	
	while(EECR & (1<<EEWE));//Wait for completion of previous write
	
	EEAR = address;//Set up address and data registers
	EEDR = data;
	
	EECR |= (1<<EEMWE);//Write logical one to EEMWE
	EECR |= (1<<EEWE);//Start EEPROM write by setting EEWE
}

void LoadRCdata()
{
	unsigned int i;
	for(i=0; i<EEPROM_SIZE_USED; i++)
	{
		RCdata[i] = EEPROM_read(i);
	}
}

void SaveRCdata()
{
	unsigned int i;
	for(i=0; i<EEPROM_SIZE_USED; i++)
	{
		EEPROM_write(i, RCdata[i]);
	}
}

void SaveRelayOn(unsigned char relay)
{
	EEPROM_write(EEPROM_RELAYS_ON, EEPROM_read(EEPROM_RELAYS_ON)|(1<<relay));
}

void SaveRelayOff(unsigned char relay)
{
	EEPROM_write(EEPROM_RELAYS_ON, EEPROM_read(EEPROM_RELAYS_ON)&(~(1<<relay)));
}

void EnableINT0()
{
	MCUCR = 0b00000010;//falling edge INT0
	GICR = 0b01000000;//enable INT0
}

void DisableINT0()
{
	GICR = 0b00000000;//disable INT0
}

unsigned char ReadADC(unsigned char channel)//max. 255
{
	ADMUX = channel;     // Channel selection

	ADCSRA |= _BV(ADSC);               // Start conversion
	while(!bit_is_set(ADCSRA,ADIF));   // Loop until conversion is complete
	ADCSRA |= _BV(ADIF);               // Clear ADIF by writing a 1 (this sets the value to 0)
	
	return((unsigned char)(ADC/4));
}

void RelayOn(unsigned char relay)
{
	switch(relay)
	{
		case RELAY0: Relay0_on();break;
		case RELAY1: Relay1_on();break;
		case RELAY2: Relay2_on();break;
		case RELAY3: Relay3_on();break;
		case RELAY4: Relay4_on();break;
		case RELAY5: Relay5_on();break;
	}	
}

void RelayOff(unsigned char relay)
{
	switch(relay)
	{
		case RELAY0: Relay0_off();break;
		case RELAY1: Relay1_off();break;
		case RELAY2: Relay2_off();break;
		case RELAY3: Relay3_off();break;
		case RELAY4: Relay4_off();break;
		case RELAY5: Relay5_off();break;
	}
}

unsigned char RelayRead(unsigned char relay)
{
	switch(relay)
	{
		case RELAY0: return Relay0_read();
		case RELAY1: return Relay1_read();
		case RELAY2: return Relay2_read();
		case RELAY3: return Relay3_read();
		case RELAY4: return Relay4_read();
		case RELAY5: return Relay5_read();
		default: return 0;
	}
}

void RelayKeyReceived(unsigned char relay)
{
	if(relayMode[relay] == ON_STOP_OFF)//the first push of the TX button activates the relay, the second push deactivates the relay
	{
		if(RelayRead(relay) == 0 && relayInfo[relay] == NOTHING)//relay not active
		{
			RelayOn(relay);
			SaveRelayOn(relay);
		}
		else//relay is active
		{
			if(relayInfo[relay] == RELAY_ACTIVE_ON_STOP_OFF_MODE)
			{
				RelayOff(relay);
				SaveRelayOff(relay);
			}
		}
	}
	else if(relayMode[relay] == ON_HOLD_OFF)//relay is active until button is released
	{
		RelayOn(relay);
		SaveRelayOff(relay);
	}
	else if(relayMode[relay] == ON_WAIT_OFF)//the first push of the TX button activates the relay, after specific time the relay deactivates, another push means immediate relay deactivation
	{
		SaveRelayOff(relay);
		if(relayInfo[relay] == NOTHING/* || relay0info == RELAY_ACTIVE_ON_WAIT_OFF_MODE*/)
		{
			RelayOn(relay);
			relayDelay[relay] = ReadADC(relay);
			relayInfo[relay] = RELAY_ACTIVE_ON_WAIT_OFF_MODE;
		}
		if(relayInfo[relay] == RELAY_ACTIVE_ON_WAIT_OFF_MODE_DEACTIVATE_NEXT_TIME)
		{
			RelayOff(relay);
			relayDelay[relay] = 0;
		}
	}
}

void RelayNoKey(unsigned char relay)
{
	if(relayMode[relay] == ON_STOP_OFF)//the first push of the TX button activates the relay, the second push deactivates the relay
	{
		if(RelayRead(relay) != 0)
		{
			relayInfo[relay] = RELAY_ACTIVE_ON_STOP_OFF_MODE;
		}
		else
		{
			relayInfo[relay] = NOTHING;
		}
	}
	else if(relayMode[relay] == ON_HOLD_OFF)//relay is active until button is released, if TX signal is lost, relay stays active until any button is pressed
	{
		RelayOff(relay);
	}
	else if(relayMode[relay] == ON_WAIT_OFF)//the first push of the TX button activates the relay, after specific time the relay deactivates, another push means immediate relay deactivation
	{
		if(relayInfo[relay] == RELAY_ACTIVE_ON_WAIT_OFF_MODE)
		{
			relayInfo[relay] = RELAY_ACTIVE_ON_WAIT_OFF_MODE_DEACTIVATE_NEXT_TIME;
		}
		else if(relayInfo[relay] == RELAY_ACTIVE_ON_WAIT_OFF_MODE_DEACTIVATE_NEXT_TIME && relayDelay[relay] == 0)
		{
			RelayOff(relay);
			relayInfo[relay] = NOTHING;
		}
	}
}

void RelayReadMode(unsigned char relay)
{
	unsigned char ADCvalue = ReadADC(relay);
	unsigned char relayWasMode = relayMode[relay];
	
	if(ADCvalue == 0)
	{
		relayMode[relay] = ON_HOLD_OFF;
	}
	else if(ADCvalue > 250)//maximum value is 255
	{
		relayMode[relay] = ON_STOP_OFF;
	}
	else
	{
		relayMode[relay] = ON_WAIT_OFF; 
	}
	
	if(relayWasMode != relayMode[relay])
	{
		//RelayOff(relay);
		relayInfo[relay] = NOTHING;
		relayDelay[relay] = 0;
	}
}

/*
EEPROM structure:
////////////////////////////////////////////////////////////////////////////////////////////////////
User ID = address:	|      0       |      1       |      2       |............byte
					|Allowed relays|Allowed relays|Allowed relays|............info
////////////////////////////////////////////////////////////////////////////////////////////////////
Example:
////////////////////////////////////////////////////////////////////////////////////////////////////
User ID = address:	|      0       |      1       |      2       |............byte
					|  0b00001111  |  0b00000001  |  0b00101010  |............info
////////////////////////////////////////////////////////////////////////////////////////////////////

There can be 256 (EEPROM_SIZE/2) remote controllers (RCs) (transmitters) to one receiver
(Allowed relays == 0) means that this remote controller with this ID can't controll this receiver
(Allowed relays == 0b00001001) means that this remote controller with this ID can controll only relay0 and relay3
(Allowed relays == 0b00111111) means that this remote controller with this ID can controll every relay
*/

void EraseEEPROM()
{
	unsigned int i;
	for(i=0; i<(EEPROM_SIZE_USED); i++)
	{
		RCdata[i] = 0;
	}
	EEPROM_write(EEPROM_RELAYS_ON, 0);
	EEPROM_write(EEPROM_INFO_BYTE, 0);
	SaveRCdata();
}

void CheckEEPROM()
{
	LED_on();
	if((EEPROM_read(EEPROM_INFO_BYTE) == 0xFF) || (Button_read() == 0))//first start
	{
		EraseEEPROM();
	}
	else
	{
		_delay_ms(100);//time for Vcc to stabilize
	}
	LED_off();	
}

void RestoreRelayStateBeforeShutdown()
{
	unsigned char i, relays;
	
	if(!Jumper_read())//jumper soldered = restore all active relays
	{
		relays = EEPROM_read(EEPROM_RELAYS_ON);
	
		for(i=0;i<NUMBER_OF_RELAYS;i++)
		{
			if((relays&(0x01<<i))!=0)
			{
				_delay_ms(100);
				RelayOn(i);
			}
		}
	}
	else//jumper not soldered = all relays off
	{
		for(i=0;i<NUMBER_OF_RELAYS;i++)
		{
			RelayOff(i);
			SaveRelayOff(i);
		}
	}
}

int main(void)
{
	unsigned char relayCount = 0;
	
	PORTB = 0b01000000;//relays+jumper
	DDRB  = 0b00111111;//relays
	DDRD  = 0b10000000;
	PORTD = 0b01000100;//PD2-INT0 - RX, PD6-RC add button
	TIMSK |= 0b00000100;//timer 1 overflow interrupt enable
	
	ADCSRA = 0b10000000;
	CheckEEPROM();
	LoadRCdata();
	
	RestoreRelayStateBeforeShutdown();
	
	EnableINT0();
	StartRelayDelaytimer();//timer 0
	
    while(1)
    {
		//receivedKey is set in the INT0 interrupt, don't change it here!

		//read relay mode by ADC
		for(relayCount=0;relayCount<NUMBER_OF_RELAYS;relayCount++)
		{
			RelayReadMode(relayCount);
		}
		
		newDataReceived=0;//newDataReceived value is set in the INT0 interrupt	
		sei();//enable all interrupts

		do
		{
			_delay_ms(1);
			for(relayCount=0; relayCount<NUMBER_OF_RELAYS; relayCount++)
			{
				if((relayNoKeyTimeout[relayCount]++) >= 160)//150 minimum!
				{				
					RelayNoKey(relayCount);	
					relayNoKeyTimeout[relayCount] = 0;
				}
			}
			
			if(Button_read() == 0)//RC add button is pressed
			{
				LED_toggle();
			}
		}while(newDataReceived == 0);//wait until new data is received
		
		cli();//to ensure receivedKey won't change during data interpretation
		
		if(Button_read() != 0)//RC add button is not pressed
		{
			if((RCdata[receivedID]&(1<<(receivedKey-KEY0))) != 0)//check if the used RC can control this relay
			{
				relayNoKeyTimeout[receivedKey-KEY0] = 0;
			
				for(relayCount=0;relayCount<NUMBER_OF_RELAYS;relayCount++)
				{
					RelayKeyReceived(receivedKey-KEY0);
				}
			}
		}
		else
		{
			LED_on();
			_delay_ms(100);
			LED_off();
			_delay_ms(100);
			LoadRCdata();
			
			RCdata[receivedID] |= (1<<(receivedKey-KEY0));
			
			EEPROM_write(receivedID, EEPROM_read(receivedID) | (1<<(receivedKey-KEY0)));
		}

		receivedKey = 0;//must be here or random blinks executes receivedKey==NO_KEY because it is the last receivedKey value every time
    }
}
