
/* Hlavní program jednotky teplotního snímače
 * MSensor.c
 *
 * Created:	1.2.2014 20:00:00
 * Author:	David Müller 
 */ 

// systémové knihovny
#include <string.h>
#include <stdint.h>
#include <stdio.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/eeprom.h>
#include <util/delay.h>

// knihovny z internetu
#include "onewire.h"
#include "ds18x20.h"

// vlastní knihovny
#include "../_common/common.h"
#include "../_common/wizfi.h"


#define ERR_MORE_SENZORS		0xF0						// chyba, tato verze nepodporuje více senzorů
#define DS18X20_DECICELSIUS		1							// používat deciC

#define BTN						PA4							// vstup pro tlačítko
#define LED						PB4							// výstup pro LED

// nastavení LED a zjištění stisku tlačítka
#define LED_ON()				PORTB |= (1 << LED)
#define LED_OFF()				PORTB &= ~(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_MEAS[]	= "MEAS %hhu %hhu.%hhu.%hhu.%hhu %hhu";
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 %hhu";
const char PROGMEM fmt_RST[]	= "RST";
const char PROGMEM rep_OK[]		= "OK";
const char PROGMEM rep_ERR[]	= "ERR";
const char PROGMEM rep_ARG[]	= "ARG";
const char PROGMEM rep_INFO[]	= "MSENS1";

// inicializační konfigurace v EEPROM
uint8_t EEMEM measInterval_EE =	10;
ipAdd_t EEMEM maskIp_EE	=		0xFFFFFF00;
uint8_t EEMEM bcInterval_EE =	10;							// [sec] interval UDP vysílání
uint8_t EEMEM txPower_EE =		4;							// (0..7) výkon vysílání

// nastavení pro normální provozní režim
ipAdd_t EEMEM bcIp_EE =			0x0A0000FF;					// 10.0.0.255
ipAdd_t EEMEM moduleIp_EE =		0x0A000015;					// 10.0.0.21
ipAdd_t EEMEM gateIp_EE =		0x0A00008A;					// 10.0.0.138
text_t EEMEM ssid_EE =			{3, "Lab"};
text_t EEMEM psk_EE =			{8, "dp126761"};
text_t EEMEM id_EE =			{11,"Senzor FEKT"};
	
// nastavení pro konfigurační režim
ipAdd_t EEMEM fs_maskIp_EE		= 0xFFFFFF00;
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 volatile actStatus_t		measStatus;						// stav měření teploty
static volatile actStatus_t		bcStatus = IDLE;				// stav UDP vysílání
static volatile uint8_t			measInterval;					// interval měření
static volatile uint8_t			bcInterval;						// interval vysílání
static volatile uint8_t			t10ms = 100;					// čítač 10ms
static volatile uint8_t			t1s = 60;						// čítač 1s

static volatile uint8_t			tMeas;							// čítání intervalu měření
static volatile uint8_t			tBc;							// čítání broadcast intervalu
static volatile uint8_t			tLED = 0;						// čítání svitu LED

static temprec_t				current = {UNDEF_T, MAX_AGE};	// záznam aktuální teploty
static temprec_t				minimum = {UNDEF_T, MAX_AGE};	// záznam minimální teploty
static temprec_t				maximum = {UNDEF_T, MAX_AGE};	// záznam maximální teploty
static cid_t					tcpDataServerCID = WZF_NO_CID;	// index soketu pro data
static cid_t					tcpSetupServerCID = WZF_NO_CID;	// index soketu pro konfiguraci
static cid_t					bcSenderCID = WZF_NO_CID;		// index soketu pro vysílání UDP
static uint8_t					sensorError = OW_PRESENCE_ERR;	// poslední chyba senzoru
static uint8_t					sensorFamily;					// identifikace senzoru
static bool_t					normal;							// normální/nastavovací režim

//---------------------------------------------------------
// deklarace interních metod
//---------------------------------------------------------

// vyřízení konfigurační zprávy
static void	setupExecute (cid_t cid, ipAdd_t ip, char* data);
// vyřízení dotazu na data
static void	dataExecute (cid_t cid, ipAdd_t ip, char* data);
// odeslání dat měření (UDP vysílání nebo odpověď na TCP dotaz)
static void dataSend(cid_t cid);
// zahájení měření
static void	startMeas(void);
// získání výsledku měření
static void	getMeas(void);
// inicializace senzoru
static void	sensor_init (void);
// bliknutí LED
static void	ledBlink(const uint8_t time);

//---------------------------------------------------------
// main
//---------------------------------------------------------

int main(void) 
{
	wzf_result_t r;
	
	// vstupy / výstupy:
	DDRA = 0;
	PORTA = (1 << BTN);
	DDRB = (1 << LED);
	//PORTB = (1 << LED);
	
	// nastavení časovače TCNT2 pro autoreload každých 10 ms
	TIMSK |= (1 << OCIE2);								// preruseni pri TCNT2==OCR2	
	TCCR2 |= (1 << WGM21) | (1 << CS22) | (1 << CS21);	// autoreload a f/256
	OCR2 = F_CPU/256/100;								// 10 ms (1/100 s, f/256)
	
	measInterval = eeprom_read_byte(&measInterval_EE);	
	tMeas = measInterval;
	measStatus = REQUIRED;
	
	bcInterval = eeprom_read_byte(&bcInterval_EE);		// [sec] interval bc vysílání (0=nevysílat)
	tBc = bcInterval;
	
	LED_ON();
	
	wzf_init();	
	sei();
	normal = !BTN_PRESSED();
	wzf_start();	
		
	if (!normal)
	{
		// režim konfigurace (po stisku tlačítka)
		//---------------------------------------------------------
		
		FS_SETUP:
		
		LED_OFF();
		_delay_ms(1000);				
		LED_ON();
		_delay_ms(250);
		LED_OFF();
		_delay_ms(250);
		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											// pro setup min.výkon
			) != WZF_OK)
		{
			wzf_reset();
			goto FS_SETUP;
		}
		
		if (wzf_associate() != WZF_OK)
		{
			//_delay_ms(5000);
			goto FS_SETUP;
		}
		
		r = wzf_openTCPServer(SETUP_PORT, &setupExecute, &tcpSetupServerCID);
		if (r != WZF_OK)
		{
			goto FS_SETUP;
		}
		
		//while(BTN_PRESSED());
		LED_OFF();
		
		for (;;)
		{
			wzf_exec();
		}
	}
	
	// provozní režim -> připojení k definovanému AP
	//---------------------------------------------------------
		
	SETUP:
	// inicializace WLAN modulu
	
	LED_OFF();
	_delay_ms(1000);
	LED_ON();
			
	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))				// pro WizFi je 0 max. a 7 min.výkon
		) != WZF_OK)
	{
		//_delay_ms(5000);
		wzf_reset();
		goto SETUP;
	}
	
	ASSOCIATE:
	// asociace WLAN modulu
	
	if (wzf_associate() != WZF_OK)
	{
		//disp_err(2);
		//_delay_ms(5000);
		goto SETUP;
	}
	
	r = wzf_openTCPServer(SETUP_PORT, &setupExecute, &tcpSetupServerCID);
	if (r == WZF_DIS)
		goto ASSOCIATE;		
	if (r != WZF_OK)
		goto SETUP;

	r = wzf_openTCPServer(DATA_PORT, &dataExecute, &tcpDataServerCID);
	if (r == WZF_DIS)
		goto ASSOCIATE;
	if (r != WZF_OK)
		goto SETUP;
	
	r = wzf_openUDPSender(&bcIp_EE, BC_PORT, &bcSenderCID);
	if (r == WZF_DIS)
		goto ASSOCIATE;
	if (r != WZF_OK)
		goto SETUP;
		
	LED_OFF();
	
	for (;;)
	{			
		if (wzf_exec() == WZF_DISASSOCIATED)
			goto ASSOCIATE;
		
		switch (measStatus)
		{
			case INPROGRESS:
				getMeas();
				if (measStatus == IDLE)
				{
					//memcpy_P(debugMsg, PSTR("T="),2);
					//DS18X20_format_from_decicelsius (current.val, &debugMsg[2], 10 );
					//debugOut();
				}
				break;
			case REQUIRED:
				startMeas();
				break;
			case ERROR:	
				measStatus = IDLE;
				tMeas = measInterval;	
			default:
				break;		
		}
					
		if (bcStatus == REQUIRED)
		{
			bcStatus = IDLE;
			dataSend(bcSenderCID);
			ledBlink(BLINK_100ms);
		}
	}	
	
}

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

// odeslání dat měření (UDP vysílání nebo odpověď na TCP dotaz)
static void dataSend(cid_t cid)
{
	wzf_sendOpen(cid);
	wzf_sendPushText_P(fmt_DATA, 4);
	wzf_sendPushInt(current.val);
	wzf_sendPushUInt(current.age);
	wzf_sendPushInt(minimum.val);
	wzf_sendPushUInt(minimum.age);
	wzf_sendPushInt(maximum.val);
	wzf_sendPushUInt(maximum.age);
	
	wzf_sendDo();	
}

// vyřízení dotazu na data
static void dataExecute(cid_t cid, ipAdd_t ip, char* data)
{
	if (strncmp_P(data, fmt_DATA, 4) == 0)
	{
		if (data[4] == '?')
		{
			dataSend(cid);
			ledBlink(BLINK_500ms);
		}
		else
			wzf_send_P(cid,rep_ARG);
	}
	else
		wzf_send_P(cid,rep_ERR);
	
	wzf_testAT();	
	wzf_close(cid);
	
}

// vyřízení konfigurační zprávy
static void setupExecute(cid_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("MSENS1"), 6);
			wzf_sendPushText_EE((char *)&id_EE.text, eeprom_read_byte(&id_EE.length));
			wzf_sendDo();
						
			//wzf_send_P(cid,rep_INFO);
			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 DATA? (teploty) DATA! (mazání min/max)
	//---------------------------------------------------------
		
	if (strncmp_P(data, fmt_DATA, 4) == 0)
	{
		if (data[4] == '?')
		{
			dataSend(cid);
			ledBlink(BLINK_500ms);
			return;
		}		
		if (data[4] == '!')
		{
			// DATA!
			minimum.val = current.val;
			minimum.age = 0;
			maximum.val = current.val;
			maximum.age = 0;
			wzf_send_P(cid, rep_OK);
			ledBlink(BLINK_500ms);
			return;
		}	
		wzf_send_P(cid, rep_ARG);
		return;
	}
		
	// povel MEAS (měření a vysílání)
	//---------------------------------------------------------		
			
	// MEAS measInterval bcIp bcInterval
	if (strncmp_P(data, fmt_MEAS, 4) == 0)
	{
		if (data[4] == '?')
		{
			// dotaz na nastavení zdroje teploty
			wzf_sendOpen(cid);
			wzf_sendPushText_P(fmt_MEAS, 4);
			wzf_sendPushUInt(eeprom_read_byte(&measInterval_EE));
			wzf_sendPushIp_EE(&bcIp_EE);
			wzf_sendPushUInt(eeprom_read_byte(&bcInterval_EE));
			wzf_sendDo();
			ledBlink(BLINK_500ms);
			return;
		}
		
		uint8_t argIp[4];
		uint8_t argMeas, argBc;
		
		if (sscanf_P(data, fmt_MEAS, &argMeas, &argIp[3], &argIp[2], &argIp[1], &argIp[0], &argBc) == 6)
		{
			if (argMeas < 5 || (argBc && (argBc < 5)))
			{
				wzf_send_P(cid, rep_ARG);
				return;
			}
			eeprom_update_block(&argIp, &bcIp_EE, 4);
			eeprom_update_byte(&bcInterval_EE, argBc);
			eeprom_update_byte(&measInterval_EE, argMeas);
			wzf_send_P(cid, rep_OK);
			ledBlink(BLINK_500ms);			
			bcInterval = argBc;
			tBc = argBc;
		}
		else
			wzf_send_P(cid, rep_ARG);
		return;
	}
	
	// povel NET (ip adresa, maska, brána)
	//---------------------------------------------------------	
		
	// NET selfIp mask gateIp
	if (strncmp_P(data, fmt_NET, 3) == 0)
	{		
		if (data[3] == '?')
		{
			// NET? - 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();
			ledBlink(BLINK_500ms);
			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);
			ledBlink(BLINK_500ms);
		}
		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);
			//DP: ohledně délky a znaků možno citovat IEEE Std 802.11™-2 2 012
			
			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();
		SW_RESET();
	}	
	
	// neznámý příkaz
	wzf_send_P(cid, rep_ERR);		
}

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

// zahájení měření
static void startMeas(void)
{
	if (sensorError)
		sensor_init();

	if (sensorError)
		return;
	
	sensorError = DS18X20_start_meas (DS18X20_POWER_EXTERN, NULL);	
	
	if (sensorError)
		return;
		
	measStatus = INPROGRESS;
}

// získání výsledku měření
static void getMeas(void)
{
	if (measStatus != INPROGRESS)
		return;													// neprobíhá měření
		
	if (DS18X20_conversion_in_progress())
		return;													// ještě není výsledek
	
	// měření bylo dokončeno
	
	measStatus = IDLE;
	sensorError = DS18X20_read_decicelsius_single (sensorFamily, &current.val);
		
	if (sensorError)
		// chyba, stávající data nejsou ovlivněna
		return;
			
	// platný výsledek, aktualizace dat
	current.age = 0;
	if (minimum.val == UNDEF_T || current.val < minimum.val)
	{
		minimum.val = current.val;
		minimum.age = 0;
	}
	if (maximum.val == UNDEF_T || current.val > maximum.val)
	{
		maximum.val = current.val;
		maximum.age = 0;
	}	
}

// inicializace senzoru
static void sensor_init (void)
{	
	uint8_t id[OW_ROMCODE_SIZE];

	ow_reset();
	sensorError = OW_SEARCH_FIRST;	
	
	DS18X20_find_sensor (&sensorError, &id[0]);

	if (!sensorError)		// nebo-li sensorError = OW_LAST_DEVICE
	{
		sensorFamily = id[0]; //	*family = id[0];
		return;
	}
	
    if (sensorError == OW_PRESENCE_ERR || sensorError == OW_DATA_ERR)
	{
		return;
	}
	sensorError = ERR_MORE_SENZORS;
}

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

ISR (TIMER2_COMP_vect)
{
	// každých 10 ms
	wzf_tick();
	
	if (tLED)
	{
		tLED--;
		if (!tLED)
		LED_OFF();
	}
	
	t10ms--;
	if (!t10ms)
	{
		// každou sec.
		t10ms = 100;
		
		tMeas--;
		if (!tMeas)
		{
			tMeas = measInterval;
			measStatus = REQUIRED;
		}
		
		if (bcInterval)
		{
			tBc--;
			if (!tBc)
			{
				tBc = bcInterval;						
				bcStatus = REQUIRED;
			}
		}
		
		t1s--;
		if (!t1s)
		{
			t1s = 60;
			
			// každou min. stárnutí teplot
		
			if (current.age < MAX_AGE)
				current.age++;
			
			if (minimum.age < MAX_AGE)
				minimum.age++;
			
			if (maximum.age < MAX_AGE)
				maximum.age++;
		}
		
	}

}