
/* Programový modul pro obsluhu WLAN modulu WizFi210
 * wizfi.c
 *
 * Created:	1.2.2014 20:00:00
 * Author:	David Müller 
 */ 

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

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

#define UART_BAUD_RATE 115200								// výchozí rychlost UART
#define WIZFI_RX_BUFFER_SIZE 128							// délka bufferu pro příjem
#define WIZFI_TX_BUFFER_SIZE 128							// délka bufferu pro vysílání

#define MAX_CID 15											// poslední index v tabulce connTab
#define AT_TIME 100											// [x10ms] timeout pro běžné AT příkazy (1 s)
#define CONN_TIME 500										// [x10ms] timeout pro AT příkazy navazování spojení (5 s)
#define TTD_TCP 30											// [s] čas do odstranění opuštěného spojení

#define NO_IP 0x00000000									// nedefinovaná ip adresa

// řetězce pro rozpoznávání odpovědí a notifikací
const char PROGMEM rpl_OK[]				= "[OK]";
const char PROGMEM rpl_ERR[]			= "[ERR";
const char PROGMEM rpl_CONN[]			= "[CONN";
const char PROGMEM rpl_DISCO[]			= "[DISCO";
const char PROGMEM rpl_ERR_DIS[]		= "[DISAS";
const char PROGMEM rpl_DIS[]			= "[Disas";
const char PROGMEM rpl_RESET[]			= "APP Reset";

// formáty AT příkazů
const char PROGMEM cmd_AT[]				= "AT";
const char PROGMEM cmd_NCLOSE[]			= "AT+NCLOSE=%x";
const char PROGMEM cmd_E0[]				= "ATE0";
const char PROGMEM cmd_XDUM[] 			= "AT+XDUM=%u";
const char PROGMEM cmd_NDHCP[] 			= "AT+NDHCP=%u";
const char PROGMEM cmd_DHCPSRVR[]		= "AT+DHCPSRVR=%u";
const char PROGMEM cmd_NSET[] 			= "AT+NSET=";
const char PROGMEM cmd_WWPA[] 			= "AT+WWPA=";
const char PROGMEM cmd_WM[] 			= "AT+WM=%u";
const char PROGMEM cmd_WAUTH[] 			= "AT+WAUTH=%u";
const char PROGMEM cmd_WRXPS[] 			= "AT+WRXPS=%u";
const char PROGMEM cmd_WRXACTIVE[]		= "AT+WRXACTIVE=%u";
const char PROGMEM cmd_WP[]				= "AT+WP=%u";
const char PROGMEM cmd_WA[] 			= "AT+WA=";
const char PROGMEM cmd_WD[] 			= "AT+WD";
const char PROGMEM cmd_NSTCP[]			= "AT+NSTCP=%u";
const char PROGMEM cmd_NSUDP[] 			= "AT+NSUDP=%u";
const char PROGMEM cmd_NCTCP[]			= "AT+NCTCP=";
const char PROGMEM cmd_NCUDP[] 			= "AT+NCUDP=";
const char PROGMEM cmd_XRESET[] 		= "AT+XRESET";

// formát ip adresy v dekadickém zápisu
const char PROGMEM fmt_ip[]				= "%u.%u.%u.%u";

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

static char pcRxBuffer[WIZFI_RX_BUFFER_SIZE];				// buffer pro příjem
static char pcTxBuffer[WIZFI_TX_BUFFER_SIZE];				// buffer pro vysílání

static char *pcTx;											// ukazatel do bufferu pro vysílání
static char *pcRx;											// ukazatel do bufferu pro příjem

// pomocné proměnné
static cid_t uCid1 = WZF_NO_CID;
static cid_t uCid2 = WZF_NO_CID;
static ipAdd_t targetIp = NO_IP;

static wzf_wlanState_t wlanState;							// akt.stav asociace
static wzf_result_t result;									// výsledek provedené operace
static volatile uint16_t wt10ms;							// pomocný čítač
static volatile uint8_t wt10ms2;							// pomocný čítač
static volatile bool_t w1sec;								// návěst uplynutí sec.

static conn_t connTab[16];									// tabulka soketů
static const text_t *wlanSSID;								// ukazatel na SSID

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

// vloží arg. z EEPROM do tx bufferu
static void putTextArg_EE(const char *src, const uint8_t len);
// vloží ip z EEPROM do tx bufferu
static void putIp_EE(const ipAdd_t *src);
// převod hex->dec
static uint8_t x2u(const char c);
// převod dec->hex
static char u2x(const uint8_t u);
// odstranění všech záznamů z connTab
static void connRemove(void);
// ukončí a odešle AT příkaz
static void sendCmd(const uint16_t timeout);
// průběžné čtení a třídění dat z rx bufferu
static void receive(void);
// odeslání a čekání na příjem
static void sendAndReceive(const uint16_t timeout, uint8_t retry);
// prasování ip adresy
static ipAdd_t parseIp(const char *src);

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

// převod hex->dec
static uint8_t x2u(const char c)
{
	if (c>'A')
		return (c & 7)+9;
	else
		return c-48;
}
// odstranění všech záznamů z connTab
static void connRemove(void)
{
	for (uint8_t i = 0; i<16; i++)
	{
		connTab[i].executor = NULL;
		connTab[i].ttd = 0;
	}
}
// převod dec->hex
static char u2x(const uint8_t u)
{
	if (u<10)
		return (char)u + 48;
	else
		return (char)u + 55;
}
// vloží ip z EEPROM do tx bufferu
static void putIp_EE(const ipAdd_t *src)
{
	uint8_t ipOctets[4];
	eeprom_read_block((void *)&ipOctets, (const void *)src, 4);
	pcTx += sprintf_P(pcTx, fmt_ip, ipOctets[3], ipOctets[2], ipOctets[1], ipOctets[0]);
}
// prasování ip adresy
static ipAdd_t parseIp(const char *src)
{
	uint8_t argIp[4];
	ipAdd_t r;
	
	sscanf(src, "%hhu.%hhu.%hhu.%hhu", &argIp[3], &argIp[2], &argIp[1], &argIp[0]);
	memcpy(&r, &argIp, 4);
	return r;
}
// vloží arg. z EEPROM do tx bufferu
static void putTextArg_EE(const char *src, const uint8_t len)
{
	eeprom_read_block(pcTx, src, len);	
	pcTx += len;
}
// ukončí a odešle AT command
static void sendCmd(const uint16_t timeout)
{
	*(pcTx++) = CHR_CR;
	*(pcTx++) = 0;
	
	sendAndReceive(timeout, 2);
}
// průběžné čtení a třídění dat z rx bufferu
static void receive(void)
{
	static wzf_rcvState_t rcvState = WZF_IDLE;
	static char cPrev = (char)0;
	ipAdd_t senderIp;
	uint16_t c16;
	char c8;
	
	for(;;)
	{
		c16 = uart_getc();
		c8 = (char)c16;

		if (c8 == CHR_TAB)
			c8 = CHR_SPACE;

		if (c16 & UART_NO_DATA)
			return;								// nejsou data, prozatím návrat
		
		if (c16 & 0xFF00)						// jiná chyba přijímu, stornování už přijatého
		{
			pcRx = pcRxBuffer;
			rcvState = WZF_IDLE;
			return;
		}
					
		// řešení escape návěstí ------------------------------------------------------------	
		
		if (cPrev == CHR_ESC)
		{
			switch (c8)
			{
				case ESC_START:
					rcvState = WZF_DATA;			
					break;

				case ESC_USTART:
					rcvState = WZF_UDATA;			
					break;

				case ESC_END:					// návěst ukončení přenosu dat
					*(pcRx) = CHR_BRK;					// označení konce dat
				
					if (pcRx > pcRxBuffer)
					{
						if (rcvState == WZF_DATA)
						{
							// nová data z TCP segmentu jsou přijata
							uint8_t cid = x2u(pcRxBuffer[0]);
							
							if (connTab[cid].ttd)
								// prodloužení životnosti spojení, pokud není nesmrtelné
								connTab[cid].ttd = TTD_TCP;
								
							if (connTab[cid].executor != NULL)
								connTab[cid].executor(cid, connTab[cid].ip, pcRxBuffer+1);
						}
						else if (rcvState == WZF_UDATA)
						{
							// nová data z UDP datagramu jsou přijata
														
							char *p = strchr(pcRxBuffer+1, CHR_SPACE);
							p = strchr(p+1, CHR_SPACE);
														
							if (p != NULL)
								if (*(p+1) != CHR_ESC)
								{
									uint8_t cid = x2u(pcRxBuffer[0]);
									//..connTab[cid].ttd = TTD_TCP;			// cid je živý
									senderIp = parseIp(pcRxBuffer+1);
									if (connTab[cid].executor != NULL)
										connTab[cid].executor(cid, senderIp, p+1);
								}
						}
					}
					rcvState = WZF_IDLE;
					break;
				case ESC_ACK:
					if (result == WZF_ACK)
						result = WZF_OK;
					else
						result = WZF_ACK;
					break;
				case ESC_FAIL:
					rcvState = WZF_IDLE;
					result = WZF_FAIL;
					break;
				default:
					break;
			} // switch
			pcRx = pcRxBuffer;						// návrat ukazatele na začátek bufferu
			cPrev = c8;
			continue;								// pokračování v přijímu dat
		} //if (cPrev == CHR_ESC) ---------------------------------------------------------------
		
		
		
		// řešení odpovědi
			
		if (c8 == CHR_LF)
		{
			pcRx = pcRxBuffer;
			rcvState = WZF_REPLY;
		}
		else if (c8 == CHR_CR)
		{			
			// končí příjem odpovědi (pozor existují i dvojité odpovědi jako [OK]...[CONNECTION...
			rcvState = WZF_IDLE;

			if (pcRx > pcRxBuffer) // je-li co nového v bufferu
			{							
				if (strncmp_P(pcRxBuffer, rpl_OK, 4) == 0)
				{
					// OK, příkaz se provedl
					result = WZF_OK;
				}
				else if (strncmp_P(pcRxBuffer, rpl_ERR, 4) == 0)
				{
					// chyba, příkaz se neprovedl
					result = WZF_ERR;
				}
				else if (strncmp_P(pcRxBuffer, rpl_ERR_DIS, 6) == 0)
				{
					// není asoc.WLAN, příkaz se neprovedl
					result = WZF_DIS;
				}
				else if (strncmp_P(pcRxBuffer, rpl_CONN, 5) == 0)
				{
					// zřídilo se požadované nebo příchozí spojení
					// např. [CONNECT 0 1 192.168.1.211 2099]
					// např. [CONNECT 1]
					uCid1 = x2u(pcRxBuffer[9]);
					if (pcRxBuffer[10] == ' ')
					{
						// po připojení klienta TCP
						// např. [CONNECT 0 1 192.168.1.211 2099]
						uCid2 = x2u(pcRxBuffer[11]);
						connTab[uCid2].ip = parseIp(&pcRxBuffer[13]);
						// kopie odkazu na handler definovaný pro server
						connTab[uCid2].executor = connTab[uCid1].executor;
						connTab[uCid2].ttd = TTD_TCP;
					}
				}			
				else if (strncmp_P(pcRxBuffer, rpl_DISCO, 6) == 0)
				{
					// vzdálený TCP klient se odpojil / vzdálený TCP server byl zastaven
					// např. [DISCONNECT 1]
					uCid1 = x2u(pcRxBuffer[12]);
					connTab[uCid1].executor = NULL;
					connTab[uCid1].ttd = 0;
				}
				else if (strncmp_P(pcRxBuffer, rpl_DIS, 6) == 0)
				{
					// WLAN přestala být dostupná
					// [Disassociation Event]
					connRemove();
					wlanState = WZF_DISASSOCIATED;
					result = WZF_DIS;
				}
				else if (strncmp_P(pcRxBuffer, rpl_RESET, 9) == 0)
				{
					// proběhl sw reset
					// APP Reset-APP SW Reset
					connRemove();
					wlanState = WZF_DISASSOCIATED;
					result = WZF_RESET;
				}
				pcRx = pcRxBuffer;
			}
			
			cPrev = c8;
			continue;								// pokračování v přijímu dat
		} //if (cCurr == CHR_CR)
			
		// uložení přijatého znaku

		if (rcvState != WZF_IDLE && (c8 > (char)31 || c8 == CHR_TAB)) // strchr_P(...)
			*(pcRx++) = c8;
		
		// kontrola přetečení bufferu
		
		if (pcRx == pcRxBuffer + WIZFI_RX_BUFFER_SIZE)
		{
			pcRx = pcRxBuffer;
			rcvState = WZF_IDLE;
		}
		
		// úschova posledního znaku	
		cPrev = c8;			
	} // for(;;)
}
// odeslání a čekání na příjem
static void sendAndReceive(uint16_t timeout, uint8_t retry)
{
	for (uint8_t r = 0; r < retry; r++)
	{
		uart_puts(pcTxBuffer);
		wt10ms = timeout;
		result = WZF_NORSULT;
			
		while (wt10ms && (result == WZF_NORSULT || result == WZF_ACK))
		{
			receive();
		}
		
		if (result == WZF_OK || result == WZF_RESET)
			return;

		if (!wt10ms)
			result = WZF_TIMEOUT;		
	}
}

//---------------------------------------------------------
// definice eterních metod
//---------------------------------------------------------

// čekání nastavenou dobu (x10ms)
void wzf_wait(uint16_t timeout)
{
	wt10ms = timeout;
	while (wt10ms);
}

// inicializace UART
void wzf_init(void)
{
	result = WZF_NORSULT;
	wlanState = WZF_DISASSOCIATED;
	pcRx = pcRxBuffer;
	pcTx = pcTxBuffer;
	uart_init(UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU));
}

// vyprázdnění UART před zahájením komunukace
void wzf_start(void)
{
	wzf_wait(500);
	while (uart_getc() != UART_NO_DATA);
}

// test AT komunikace
wzf_result_t wzf_testAT()
{
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_AT);
	sendCmd(AT_TIME);
	return result;
}

// provede nastavení modulu podle zadaných parametrů	
wzf_result_t wzf_setup (
	const ipAdd_t *selfIp, const ipAdd_t *maskIp, const ipAdd_t *gateIp, wzf_dhcp_t dhcp,
	const text_t *ssid, const text_t *psk, const wzf_wlanMode_t mode, const uint8_t txPower)
{
	connRemove();
	wlanSSID = ssid;							// uložení odkazu na ssid kvůli reasoc.
	
	for (uint8_t i = 0; i<3; i++)
	{	
		wzf_wait(50);								// 500 ms
		//while (uart_getc() != UART_NO_DATA);
		pcRx = pcRxBuffer;
		pcTx = pcTxBuffer;
		
		//DP: mezi buffer, aby se zjednodušilo/zpřehlednilo opakování při chybě
		//putATcmd_P(text_E0);
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_E0);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;
		
		//putATcmd_P(text_XDUM);
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_XDUM, 0);
		//putArg(0);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;
			
		//putATcmd_P(text_WD);
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WD);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;	

		// klient DHCP			
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_NDHCP, dhcp);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;

		// nastavení IP adres
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_NSET);
	
		putIp_EE(selfIp);

		*(pcTx++) = ',';
		putIp_EE(maskIp);
	
		*(pcTx++) = ',';
		putIp_EE(gateIp);
	
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;

		if (mode == WZF_AP)
		{
			// nastavení funkce DHCP serveru (pouze pro AP)
			pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_DHCPSRVR, 1);
			sendCmd(AT_TIME);
			// na výsledku nezáleží (OK je OK, ERROR znamená, že už bylo nastaveno)
		}
										
		uint8_t len = eeprom_read_byte(&psk->length);
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WAUTH,(!len ? 0 : 1));
		//putArg((!len ? 0 : 1));
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;
		
		if (len)
		{
			pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WWPA);
			putTextArg_EE(psk->text, len);
			sendCmd(AT_TIME);
			if (result != WZF_OK)
				continue;
		}
	
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WM, mode);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;

		// nastavení úsporného režimu
		
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WRXPS, 1);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;
		
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WRXACTIVE, 0);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;

		
		pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WP, txPower);
		sendCmd(AT_TIME);
		if (result != WZF_OK)
			continue;
					
		return WZF_OK;					
	}
	return WZF_ERR;
}

// připojení/zřízení AP
wzf_result_t wzf_associate(void)
{
	uint8_t len = eeprom_read_byte(&wlanSSID->length);
	//if (!len)
		//return WZF_ARGERR;
		
	//putATcmd_P(text_WA);
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WA);
	putTextArg_EE(wlanSSID->text, len);
	sendCmd(2000);
	if (result == WZF_OK)
		wlanState = WZF_ASSOCIATED;
	return result;
}

// odpojení/zrušení AP		
wzf_result_t wzf_disassociate(void)
{
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_WD);
	sendCmd(AT_TIME);
	if (result == WZF_OK)
		connRemove();
	return result;
}

// uzavření definovaného spojení/vysílání
wzf_result_t wzf_close(const cid_t cid)
{
	uCid1 = WZF_NO_CID;
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_NCLOSE, cid);
	sendCmd(CONN_TIME);
	if (result == WZF_OK)
	{
		connTab[cid].executor = NULL;
		connTab[cid].ttd = 0;
	}
	return result;
}

// otevře TCP port pro poslech (nenavazuje spojení, tj. přes tento cid se nekomunikuje)
wzf_result_t wzf_openTCPServer(const uint16_t port, const executor_t handler, cid_t *cid)
{
	uCid1 = WZF_NO_CID;
	targetIp = NO_IP;
		
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_NSTCP, port);
	sendCmd(AT_TIME);
	if (result == WZF_OK && uCid1 != WZF_NO_CID)
	{
		connTab[uCid1].executor = handler;
		connTab[uCid1].ttd = 0;					// pro poslech není čas omezen
		connTab[uCid1].ip = NO_IP;				// tento záznam není spojení, ip budou mít v záznamu až připojení klienti
		*cid = uCid1;
	}
	return result;
}

// otevře poslech na UDP portu
wzf_result_t wzf_openUDPListener(const uint16_t port, const executor_t handler, cid_t *cid)
{
	uCid1 = WZF_NO_CID;
	targetIp = NO_IP;
	
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_NSUDP, port);
	sendCmd(AT_TIME);
	
	if (result == WZF_OK && uCid1 != WZF_NO_CID)
	{
		connTab[uCid1].executor = handler;
		connTab[uCid1].ttd = 0;					// pro poslech není čas omezen
		connTab[uCid1].ip = NO_IP;
		*cid = uCid1;
	}
	return result;
}

// otevře TCP spojení se vzdáleným serverem (přes tento cid se bude komunikovat)
wzf_result_t wzf_openTCPClient(const ipAdd_t *ip, const uint16_t port, executor_t handler, cid_t *cid)
{	
	uCid1 = WZF_NO_CID;
	targetIp = eeprom_read_dword(ip);
	
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_NCTCP);
	putIp_EE(ip);

	*(pcTx++) = ',';
	pcTx += sprintf(pcTx, "%u", port);
			
	sendCmd(CONN_TIME);
	if (result == WZF_OK && uCid1 != WZF_NO_CID)
	{
		connTab[uCid1].executor = handler;
		connTab[uCid1].ttd = TTD_TCP;			// omezení životnosti při nečinnosti
		connTab[uCid1].ip = targetIp;
		// memcpy(&(connTab[uCid1].ip),&ip[0],4);
		*cid = uCid1;
	}
	return result;
}

// vytvoří soket pro odesílání UDP datagramů
wzf_result_t wzf_openUDPSender(const ipAdd_t *ip, const uint16_t port, cid_t *cid)
{
	uCid1 = WZF_NO_CID;
	targetIp = eeprom_read_dword(ip);
	
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_NCUDP);
	putIp_EE(ip);
	*(pcTx++) = ',';
	//pcTx += sprintf(pcTx, "%u", eeprom_read_word((const void *)port));
	pcTx += sprintf(pcTx, "%u", port);
	sendCmd(AT_TIME);
		
	if (result == WZF_OK && uCid1 != WZF_NO_CID)
	{
		*cid = uCid1;
		connTab[uCid1].executor = NULL;
		connTab[uCid1].ip = targetIp;
		connTab[uCid1].ttd = 0;									// nekonečná životnost
		//memcpy(&(connTab[uCid1].ip),&ip[0],4);		
	}
	
	return result;
}

// vyslání dat z prog.úpaměti na indexem definovaný soket
wzf_result_t wzf_send_P(const cid_t cid, const char *data)
{
	pcTx = pcTxBuffer;
	pcTx += sprintf_P(pcTx, PSTR("\eS%u"), cid);
	pcTx += strlcat_P(pcTx, data, WIZFI_TX_BUFFER_SIZE-5);
	//*(pcTx++) = CHR_ETX;
	sprintf_P(pcTx, PSTR("\eE"));
	sendAndReceive(AT_TIME, 2);
	return result;	
}

// zahájení vysílání na indexem definovaný soket
void wzf_sendOpen(const cid_t cid)
{
	pcTx = pcTxBuffer;
	// odeslání přes buffer, protože je při neúspěchu potřeba opakovat
	*(pcTx++) = CHR_ESC;
	*(pcTx++) = ESC_START;
	*(pcTx++) = u2x(cid);
	//*(pcTx++) = CHR_BRK;
	if (connTab[cid].ttd)
		// prodložení životnosti spojení (pokud není nesmrtelné)
		connTab[cid].ttd = TTD_TCP;
}

// vložení řetězce z prog.paměti na indexem definovaný soket pro odeslání
void wzf_sendPushText_P(const char *src, const uint8_t len)
{
	strcpy_P(pcTx, src);
	pcTx += len;
	*(pcTx++) = CHR_SPACE;	
}

// vložení řetězce z EEPROM na indexem definovaný soket pro odeslání
void wzf_sendPushText_EE(const char *src, const uint8_t len)
{
	putTextArg_EE(src, len);
	*(pcTx++) = CHR_SPACE;
}

// vložení ip z EEPROM na indexem definovaný soket pro odeslání
void wzf_sendPushIp_EE(const ipAdd_t *src)
{
	putIp_EE(src);
	*(pcTx++) = CHR_SPACE;
}

// vložení čísla UInt na indexem definovaný soket pro odeslání
void wzf_sendPushUInt(const uint16_t val)
{
	pcTx += sprintf_P(pcTx, PSTR("%u"), val);
	*(pcTx++) = CHR_SPACE;
}

// vložení čísla Int na indexem definovaný soket pro odeslání
void wzf_sendPushInt(const int16_t val)
{
	pcTx += sprintf_P(pcTx, PSTR("%d"), val);
	*(pcTx++) = CHR_SPACE;
}

// dokončení odeslání
wzf_result_t wzf_sendDo()
{
	pcTx--;										// návrat - potlačení poslední vložené mezery
	//*(pcTx++) = CHR_ETX;
	//strcat_P(pcTx, PSTR("\eE"));
	strcpy_P(pcTx, PSTR("\eE"));
	sendAndReceive(AT_TIME, 2);
	return result;
}

// sw reset modulu WizFi
void wzf_reset(void)
{
	//putATcmd_P(text_XRESET);
	wzf_disassociate();
	//sendAndReceive(1000,1);
	pcTx = pcTxBuffer + sprintf_P(pcTxBuffer, cmd_XRESET);
	*(pcTx++) = CHR_CR;
	//*(pcTx++) = 0;		
	uart_puts(pcTxBuffer);
	wzf_wait(1000);
	connRemove();
}

// obsluha příjmu z UART (data ze soketů a notifikační zprávy)
wzf_wlanState_t wzf_exec(void)
{
	if (w1sec)
	{
		w1sec = FALSE;
		for (uint8_t i = 0;i < MAX_CID; i++)
		{
			if (connTab[i].ttd == 0)
				// spojení je už uzavřené nebo je nesmrtelné, pokračovat na další
				continue;
				
			if (connTab[i].ttd)
				connTab[i].ttd--;
				
			if (connTab[i].ttd == 0)
				// pokus o uzavření opuštěného spojení, neúspěch neřešen, protože uzavřít ještě může
				// i protistrana, řeší se až teprve, kdyby došlo k vyčerpání cid
				wzf_close(i);
		}		
	}
	receive();				// průběžný příjem, výsledek nikoho nezajímá, zpracování výhradně přes callback
	return wlanState;
}

// časování - voláno každých 10ms z hlavního programu
void wzf_tick(void)
{
	if (wt10ms)
		wt10ms--;
		
	if (wt10ms2)
		wt10ms2--;
	else		
	{
		wt10ms2 = 100;
		w1sec = TRUE;
	}	
}
