
/* Hlavní program jednotky zobrazovače
 * MDisplay.h
 *
 * Created:	1.2.2014 20:00:00
 * Author:	David Müller 
 */ 

#include <avr/io.h>
#include <string.h>
#include <stdio.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/eeprom.h>

#include "led_disp.h"
#include "../_common/wizfi.h"
#include "../_common/common.h"

#define LED						PA3							// výstup pro oranžovou LED ("FUNKCE")
#define BTN						PA4							// vstup tlačítka
#define INTERVAL_FAIL			30							// [sec] opakování dotazu po neúspěchu

// nastavení LED a zjištění stisku tlačítka
#define LED_ON()				PORTA |= (1 << LED)
#define LED_OFF()				PORTA &= ~(1 << LED)
#define BTN_PRESSED()			((PINA & (1<<BTN)) == 0)

//---------------------------------------------------------
// formáty příkazů
//---------------------------------------------------------

const char PROGMEM fmt_ID[]		= "ID";
const char PROGMEM fmt_DATA[]	= "DATA %d %u %d %u %d %u";
const char PROGMEM fmt_SRC[]	= "SRC %hhu.%hhu.%hhu.%hhu %huu";
const char PROGMEM fmt_DISP[]	= "DISP %hhu %u %hhu";
const char PROGMEM fmt_NET[]	= "NET %hhu.%hhu.%hhu.%hhu %hhu.%hhu.%hhu.%hhu %hhu.%hhu.%hhu.%hhu";
const char PROGMEM fmt_WLAN[]	= "WLAN";
const char PROGMEM fmt_RST[]	= "RST";
 const char PROGMEM rq_DATA[]	= "DATA?";

//---------------------------------------------------------
// odpovědi
//---------------------------------------------------------

const char PROGMEM rep_OK[]		= "OK";
const char PROGMEM rep_ERR[]	= "ERR";
const char PROGMEM rep_ARG[]	= "ARG";

//---------------------------------------------------------
// inicializační konfigurace v EEPROM
//---------------------------------------------------------

tunit_t	EEMEM unit_EE			= CELSIUS;
uint8_t	EEMEM brightness_EE		= 0;
uint8_t	EEMEM rqstInt_EE		= 30;						// [sec] interval aktivního dotazování
uint16_t EEMEM maxAge_EE		= 30;						// [min] max. stáří teploty pro zobrazování
uint8_t EEMEM txPower_EE		= 4;						// vysílací výkon 0..7

ipAdd_t EEMEM maskIp_EE			= 0xFFFFFF00;				// 10.0.0.255
ipAdd_t EEMEM gateIp_EE			= 0x0A00008A;				// 10.0.0.138
ipAdd_t EEMEM moduleIp_EE		= 0x0A000016;				// 10.0.0.22
ipAdd_t EEMEM sensorIp_EE		= 0x0A000015;				// 10.0.0.21
text_t EEMEM ssid_EE			= {3, "Lab"};
text_t EEMEM psk_EE				= {8, "dp126761"};
text_t EEMEM id_EE =			{12,"Displej FEKT"};
	
ipAdd_t EEMEM fs_maskIp_EE		= 0xFFFFFF00;				// 255.255.255.0
ipAdd_t EEMEM fs_setupIp_EE		= 0xC0A80101;				// 192.168.1.1
text_t EEMEM fs_ssid_EE			= {11, "ModuleSetup"};
text_t EEMEM fs_psk_EE			= {0, ""};

//---------------------------------------------------------
// interní proměnné 
//---------------------------------------------------------

static int8_t udpDataCID				= WZF_NO_CID;		// index soketu pro příjem UDP
static cid_t tcpSetupServerCID			= WZF_NO_CID;		// index soketu pro konfiguraci
static int8_t tcpDataClientCID			= WZF_NO_CID;		// uložení cid v době aktivního dotazu na data
static bool_t normal;										// normální/nastavovací režim

static volatile uint8_t t100us			= 1;				// pomocný čítač
static volatile uint8_t t10ms			= 100;				// pomocný čítač
static volatile uint8_t t1s				= 15;				// za 15 sec. minutové akce
static volatile uint8_t tLED			= 0;				// pomocný čítač
static volatile uint8_t rqstInt			= INTERVAL_FAIL;	// interval dotazování na data
static volatile uint8_t tRqstInt		= INTERVAL_FAIL;	// čítač pro dotazování na data
static volatile actStatus_t rqstStatus	= FROZEN;			// stav dotazování
static volatile actStatus_t aging		= IDLE;				// stav stárnutí

static temprec_t current = {.val = UNDEF_T};				// záznam aktuální teploty
static temprec_t min = {.val = UNDEF_T};					// záznam minimální teploty
static temprec_t max = {.val = UNDEF_T};					// záznam maximální teploty

//---------------------------------------------------------
// deklarace interních metod	
//---------------------------------------------------------
// zobrazení teploty
static void dispCurrentT(void);								
// vyřízení setup zprávy
static void setupExecute(int8_t cid, ipAdd_t ip, char* data);
// vyřízení datové zprávy
static void dataExecute(int8_t cid, ipAdd_t ip, char* data);	
// převod deci°C --> deci°F se zaokrouhlením
static int16_t toFahrenheit(int16_t temp);			
// bliknutí LED		
static void ledBlink(const uint8_t time);					


int main(void) 
{
	wzf_result_t r;											// pomocné ukládání výsledku operace
				
	// nastavení časovače TCNT2 pro autoreload každých 100us
	TIMSK |= (1 << OCIE2);							// přerušení při shodě TCNT2 a OCR2
	OCR2 = 46;										// 0.1 ms při 3.6864 Mhz a děličce f/8
	TCCR2 |= (1 << WGM21) | (1 << CS21);			// autoreload a dělení f/8	*/		
	DDRA |= (1 << LED);
	
	LED_ON();
	
	disp_init(eeprom_read_byte(&brightness_EE));
	wzf_init();	
	sei();
	
	normal = !BTN_PRESSED();
	
	disp_err(0);
	wzf_start();
		
	
	if (!normal)
	{
		// režim konfigurace (po stisku tlačítka)
		//---------------------------------------------------------
				
		FS_SETUP:
		
		disp_err(1);		
		LED_OFF();
		wzf_wait(50);
		LED_ON();
		wzf_wait(25);
		LED_OFF();
		wzf_wait(25);
		LED_ON();
		
		if (wzf_setup(
			(const ipAdd_t *)&fs_setupIp_EE,
			(const ipAdd_t *)&fs_maskIp_EE,
			(const ipAdd_t *)&fs_setupIp_EE,
			WZF_DHCP_DISABLE,
			&fs_ssid_EE,
			&fs_psk_EE,
			WZF_AP,
			MIN_PWR) != WZF_OK)
		{
			wzf_reset();
			goto FS_SETUP;
		}
		
		disp_err(2);
		if (wzf_associate() != WZF_OK)
			goto FS_SETUP;
		
		if (wzf_openTCPServer(
			SETUP_PORT,
			&setupExecute,
			&tcpSetupServerCID) != WZF_OK)
		{
			goto FS_SETUP;
		}
		
		LED_OFF();
		//disp_print((uint8_t[3]){SYMBOL_S, SYMBOL_E, SYMBOL_t});
		disp_print((uint8_t[3]){0xFF, 0xFF, 0xFF});
		
		// hlavní symčka v konfiguračním režimu
		for (;;)
		{
			wzf_exec();										// obsluh WLAN modulu
		}
	}			
	
	// provozní režim -> připojení k definovanému AP
	//---------------------------------------------------------	
	
	
	SETUP:			
	// inicializace WLAN modulu
	
	disp_err(1);		
	
	if (wzf_setup(
		(const ipAdd_t *)&moduleIp_EE,
		(const ipAdd_t *)&maskIp_EE,
		(const ipAdd_t *)&gateIp_EE,
		WZF_DHCP_DISABLE,
		&ssid_EE,
		&psk_EE,
		WZF_INFRASTRUCTURE,
		(7-eeprom_read_byte(&txPower_EE))
		) != WZF_OK)
	{
		disp_err(0);
		wzf_wait(500);
		wzf_reset();
		goto SETUP;
	}
		
	ASSOCIATE:
	// asociace WLAN modulu
	
	disp_err(2);
	
	if (wzf_associate() != WZF_OK)
	{
		disp_err(0);
		wzf_wait(500);
		goto SETUP;
	}
			
	r = wzf_openTCPServer(SETUP_PORT, &setupExecute, &tcpSetupServerCID);
	if (r == WZF_DIS)
		goto ASSOCIATE;
	if (r != WZF_OK)
		goto SETUP;

	r = wzf_openUDPListener(BC_PORT, &dataExecute, &udpDataCID);
	if (r == WZF_DIS)
		goto ASSOCIATE;
	if (r != WZF_OK)
		goto SETUP;
			
	LED_OFF();
	disp_off(TRUE);											// jen dp, protože nejsou platná data
		
	
	rqstInt = eeprom_read_byte(&rqstInt_EE);				// interval aktivních dotazů
	if (rqstInt)
	{
		rqstStatus = REQUIRED;
	}
		

	// hlavní smyčka	
	for (;;)
	{
		if (wzf_exec() == WZF_DISASSOCIATED)				// osbluha WLAN modulu
			goto ASSOCIATE;									// při ztrátě asociace -> reasociace
						
		if (rqstStatus == REQUIRED)
		{
			// vznikl požadavek aktivního dotazu na teplotu
			rqstStatus = IDLE;
			
			wzf_result_t r = wzf_openTCPClient(&sensorIp_EE, DATA_PORT, &dataExecute, &tcpDataClientCID);
			if (r == WZF_OK)
			{
				r = wzf_send_P(tcpDataClientCID, rq_DATA);
				ledBlink(BLINK_500ms);
			}
					
			if (r == WZF_OK)
				tRqstInt = eeprom_read_byte(&rqstInt_EE);	// příští požadavek za běžnou dobu
			else
				tRqstInt = INTERVAL_FAIL;					// příští požadavaek za zkrácenou dobu
		}
					
		if (aging == REQUIRED)
		{
			// obsluha stárnutí teplot
			aging = IDLE;
			if (current.age < MAX_AGE)
			{
				current.age++;
				if (current.age > eeprom_read_word(&maxAge_EE))
					disp_off(TRUE);
			}
			if (min.age < MAX_AGE)
				min.age++;
			if (max.age < MAX_AGE)
				max.age++;	
				
		} //---aging
	} //---main loop
}

//---------------------------------------------------------
// definice interních metod
//---------------------------------------------------------

// vyřízení setup zprávy
static void setupExecute(int8_t cid, ipAdd_t ip, char* data)
{
		
	// povel ID (typ a verze modulu + definované pojmenování)
	//---------------------------------------------------------
		
	if (strncmp_P(data, fmt_ID, 2) == 0)
	{	
		if (data[2] == '?')
		{		
			wzf_sendOpen(cid);
			wzf_sendPushText_P(fmt_ID, 2);
			wzf_sendPushText_P(PSTR("MDISP1"), 6);
			wzf_sendPushText_EE((char *)&id_EE.text, eeprom_read_byte(&id_EE.length));
			wzf_sendDo();
			return;
		}

		char *p0 = strchr(data, CHR_BRK);
		uint8_t len = p0 - data - 3;	
		if (len > 16)
		{
			wzf_send_P(cid, rep_ARG);
			return;
		}			
		eeprom_update_byte(&id_EE.length, len);
		eeprom_update_block(data+3, &id_EE.text, len);
		wzf_send_P(cid, rep_OK);
		return;
	}
			
	// povel SRC (nastavení zdroje dat a frekvence dotazů)
	//---------------------------------------------------------
	
	if (strncmp_P(data, fmt_SRC, 3) == 0)
	{
		if (data[3] == '?')
		{
			// dotaz na nastavení zdroje teploty
			wzf_sendOpen(cid);
			wzf_sendPushText_P(fmt_SRC, 3);
			wzf_sendPushIp_EE(&sensorIp_EE);
			wzf_sendPushUInt(eeprom_read_byte(&rqstInt_EE));
			wzf_sendDo();
			return;
		}
		
		uint8_t argIp[4];
		uint8_t argInterval;
			
		if (sscanf_P(data, fmt_SRC, &argIp[3], &argIp[2], &argIp[1], &argIp[0], &argInterval) == 5)
		{
			if (argInterval && argInterval < 5)
			{
				wzf_send_P(cid, rep_ARG);
				return;				
			}
						
			eeprom_update_block(&argIp, &sensorIp_EE, 4);
			eeprom_update_byte(&rqstInt_EE, argInterval);
			wzf_send_P(cid, rep_OK);			
			rqstInt = argInterval;			
			if (argInterval)
			{
				tRqstInt = rqstInt;
				rqstStatus = IDLE;
			}
			else
				rqstStatus = FROZEN;
		}
		else
			wzf_send_P(cid, rep_ARG);
		return;
	}
	
	// povel DISP (nastavení parametrů zobrazování)
	//---------------------------------------------------------

	if (strncmp_P(data, fmt_DISP, 4) == 0)
	{		
		if (data[4] == '?')
		{
			// dotaz na nastavení zobrazování
			wzf_sendOpen(cid);
			wzf_sendPushText_P(fmt_DISP, 4);
			wzf_sendPushUInt(eeprom_read_byte(&unit_EE));
			wzf_sendPushUInt(eeprom_read_word(&maxAge_EE));
			wzf_sendPushUInt(eeprom_read_byte(&brightness_EE));
			wzf_sendDo();
			return;
		}
				
		tunit_t argUnit;
		uint8_t argBrightness;
		uint8_t argMaxAge;
		
		if (sscanf_P(data, fmt_DISP, &argUnit, &argMaxAge, &argBrightness) == 3)
		{
			if (argUnit > 1 || !argMaxAge || argBrightness > 5)
			{
				wzf_send_P(cid, rep_ARG);
				return;
			}			
			eeprom_update_byte(&unit_EE, argUnit);			
			eeprom_update_word(&maxAge_EE, argMaxAge);
			eeprom_update_byte(&brightness_EE, argBrightness);			
			wzf_send_P(cid, rep_OK);
			if (normal)
				dispCurrentT();								// v nastavovacím režimu svítí "SEt"
			disp_brightness(argBrightness);
		}
		else
			wzf_send_P(cid, rep_ARG);
		return;
	}
		
	// povel NET (ip adresa, maska, brána)
	//---------------------------------------------------------	
	
	if (strncmp_P(data, fmt_NET, 3) == 0)
	{
		
		if (data[3] == '?')
		{
			// dotaz na nastavení sítě
			wzf_sendOpen(cid);
			wzf_sendPushText_P(fmt_NET, 3);
			wzf_sendPushIp_EE(&moduleIp_EE);
			wzf_sendPushIp_EE(&maskIp_EE);
			wzf_sendPushIp_EE(&gateIp_EE);
			wzf_sendDo();
			return;
		}
				
		uint8_t argModuleIp[4];
		uint8_t argMask[4];
		uint8_t argGateIp[4];
		
		if (sscanf_P(data, fmt_NET,
			&argModuleIp[3], &argModuleIp[2], &argModuleIp[1], &argModuleIp[0],
			&argMask[3], &argMask[2], &argMask[1], &argMask[0],
			&argGateIp[3], &argGateIp[2], &argGateIp[1], &argGateIp[0]) == 12)
		{
			eeprom_update_block(&argModuleIp, &moduleIp_EE, 4);
			eeprom_update_block(&argMask, &maskIp_EE, 4);
			eeprom_update_block(&argGateIp, &gateIp_EE, 4);
			wzf_send_P(cid, rep_OK);
		}
		else
			wzf_send_P(cid, rep_ARG);
		return;
	}
	
	// povel WLAN (vysílací výkon, zabezpečení)
	//---------------------------------------------------------
	
	if (strncmp_P(data, fmt_WLAN, 4) == 0)
	{
		if (data[4] == '?')
		{
			// dotaz na nastavení připojení k AP
			// z bezp. důvodů nevrací klíč
			wzf_sendOpen(cid);
			wzf_sendPushText_P(fmt_WLAN, 4);
			wzf_sendPushUInt(eeprom_read_byte(&txPower_EE));
			wzf_sendPushText_EE((char *)&ssid_EE.text, eeprom_read_byte(&ssid_EE.length));
			wzf_sendPushUInt( eeprom_read_byte(&psk_EE.length) ? AUTH_WPA : AUTH_NONE);
			wzf_sendDo();
			ledBlink(BLINK_500ms);
			return;
		}
		
		uint8_t txPower;
		char *p0, *p1, *p2;
		
		if (sscanf_P(data, fmt_WLAN, &txPower) == 1)
		{
			p0 = strchr(data+5, CHR_SPACE);
			p1 = strchr(p0+1, CHR_SPACE);
			p2 = strchr(p1+1, CHR_BRK);
			
			uint8_t lenSsid = p1 - p0 - 1;
			uint8_t lenKey = p2 - p1 - 1;
			
			if ((txPower & ~7) || !lenSsid || (lenSsid > 32) || (lenKey > 64))
			{
				wzf_send_P(cid, rep_ARG);
				return;
			}
			eeprom_update_byte(&txPower_EE, txPower);
			
			eeprom_update_byte(&ssid_EE.length, lenSsid);
			eeprom_update_block(p0+1, &ssid_EE.text, lenSsid);
			
			eeprom_update_byte(&psk_EE.length,lenKey);			
			if (lenKey)
				eeprom_update_block(p1+1, &psk_EE.text, lenKey);
				
			wzf_send_P(cid, rep_OK);
			ledBlink(BLINK_500ms);
		}
		else
			wzf_send_P(cid, rep_ARG);

		return;
	}		
	
	if (strncmp_P(data, fmt_RST, 3) == 0)
	{		
		wzf_send_P(cid, rep_OK);
		wzf_reset();
		disp_off(FALSE);		
		SW_RESET();
	}	
	wzf_send_P(cid, rep_ERR);		
}

// vyřízení datové zprávy
static void dataExecute(int8_t cid, ipAdd_t ip, char* data)
{

	ipAdd_t sensorIp;	
	eeprom_read_block(&sensorIp, &sensorIp_EE, 4);
	
	if (sensorIp == ip)
	{		
		if (sscanf_P(data, fmt_DATA, &current.val, &current.age, &min.val, &min.age, &max.val, &max.age))
		{
			dispCurrentT();
			ledBlink(BLINK_100ms);
			// bez ohledu na to, jestli data pochází z odpovědi na dotaz nebo z UDP broadcastu, se nastaví
			// nový interval pro dotaz (v případě, že interval_EE==0, je dotazování vypnuto)
			tRqstInt = rqstInt;			
		}
	}
}

// zobrazení teploty
static void dispCurrentT(void)
{
	if ((current.val == UNDEF_T) || (current.age > eeprom_read_word(&maxAge_EE)))
	{
		// akt.teplota není definována nebo je zastaralá
		disp_off(TRUE);							// svítí pouze poslední tečka
		return;
	}
	
	if (eeprom_read_byte(&unit_EE) == CELSIUS)
		// zobrazení teploty ve °C
		disp_10(current.val);
	else
		// zobrazení teploty ve °F
		disp_10(toFahrenheit(current.val));
}

// převod deci°C --> deci°F se zaokrouhlením
static int16_t toFahrenheit(int16_t temp)
{
	if (temp < 0)
		return (18*temp + 3195)/10;
	else
		return (18*temp + 3205)/10;
}

// bliknutí LED
static void ledBlink(const uint8_t time)
{
	tLED = time;
	LED_ON();
}

//---------------------------------------------------------
// obsluhy přerušení
//---------------------------------------------------------

ISR (TIMER2_COMP_vect)
{
	// každých 100 us
	disp_tick();
			
	t100us--;
	if (!t100us)
	{
		t100us = 100;
		// každých 10 ms
		wzf_tick();
		
		if (tLED)
		{
			tLED--;
			if (!tLED)
			LED_OFF();
		}
		
		t10ms--;
		if (!t10ms)
		{			
			// každou sekundu
			t10ms = 100;			
			if (rqstStatus == IDLE)
			{
				tRqstInt--;
				if (!tRqstInt)
				{
					tRqstInt = rqstInt;
					rqstStatus = REQUIRED;
				}
			}
			
			t1s--;
			if (!t1s)				
			{
				// každou minutu
				t1s = 60;
				aging = REQUIRED;
			}
		}
	}	
}