//~ #define GSM_DEBUG 0
#define GSM_DEBUG 1

#define GSM_STATE_DISCONNECTED 0
#define GSM_STATE_CONNECTED 1
#define GSM_STATE_IDLE 89
#define GSM_STATE_FIRSTINIT 98

#define UART_GPIO_TX 19//4
#define UART_GPIO_RX 21//5
#define UART_BDRATE 115200

#define GSM_BUF_SIZE (1024)
#define GSM_OK_Str "OK"
#define GSM_APN "internet.."
#define REMOTE_PORT "80"
#define REMOTE_HOST "..."
#define WEB_URL_API "http://" REMOTE_HOST ":" REMOTE_PORT "/api"
#define AT_CIPSTART "AT+CIPSTART=\"TCP\",\"" REMOTE_HOST "\",\"" REMOTE_PORT "\"\r\n"
#define AT_CGDCONT "AT+CGDCONT=1,\"IP\",\"" GSM_APN "\"\r\n"
#define AT_SAPBR_APN "AT+SAPBR=3,1,\"APN\",\"" REMOTE_HOST "\"\r\n"

static uint8_t gsm_status = GSM_STATE_DISCONNECTED;

uart_port_t uart_num = UART_NUM_1;

typedef struct
{
	char* cmd;
	uint16_t cmdSize;
	char* cmdResponseOnOk;
	uint16_t timeoutMs;
	uint16_t delayMs;
	uint8_t skip;
}GSM_Cmd;

static GSM_Cmd cmd_AT =
{
	.cmd = (char*)"AT\r\n",
	.cmdSize = sizeof("AT\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 300,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_CIMI =//IMSI sim card ID
{
	.cmd = (char*)"AT+CIMI\r\n",
	.cmdSize = sizeof("AT+CIMI\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 300,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_GSN =//IMEI phone ID
{
	.cmd = (char*)"AT+GSN\r\n",
	.cmdSize = sizeof("AT+GSN\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 300,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_NoSMSInd =
{
	.cmd = (char*)"AT+CNMI=0,0,0,0,0\r\n",
	.cmdSize = sizeof("AT+CNMI=0,0,0,0,0\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_Reset =
{
	.cmd = (char*)"ATZ\r\n",
	.cmdSize = sizeof("ATZ\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 300,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_RFOn =
{
	.cmd = (char*)"AT+CFUN=1\r\n",
	.cmdSize = sizeof("ATCFUN=1,0\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 10000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_EchoOff =
{
	.cmd = (char*)"ATE0\r\n",
	.cmdSize = sizeof("ATE0\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 300,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_CPIN =
{
	.cmd = (char*)"AT+CPIN?\r\n",
	.cmdSize = sizeof("AT+CPIN?\r\n")-1,
	.cmdResponseOnOk = (char*)"CPIN: READY",
	.timeoutMs = 5000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_CREG =
{
	.cmd = (char*)"AT+CREG?\r\n",
	.cmdSize = sizeof("AT+CREG?\r\n")-1,
	.cmdResponseOnOk = (char*)"CREG: 0,1",
	.timeoutMs = 3000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_CGATT =//check GPRS attach
{
	.cmd = (char*)"AT+CGATT=1\r\n",
	.cmdSize = sizeof("AT+CGATT=1\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_CSTT =//set APN
{
/* 	.cmd = (char*)"AT+CSTT?\r\n",
 * 	.cmdSize = sizeof("AT+CSTT?\r\n")-1,
 */
	.cmd = (char*)"AT+CSTT=\"INTERNET\"\r\n",
	.cmdSize = sizeof("AT+CSTT=\"INTERNET\"\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 2000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_CIICR =//bring up wireless(GPRS) connection
{
	.cmd = (char*)"AT+CIICR\r\n",
	.cmdSize = sizeof("AT+CIICR\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 2000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_APN =
{
	.cmd = (char*)AT_CGDCONT,
	.cmdSize = sizeof(AT_CGDCONT),
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 2000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_ATD =
{
	.cmd = (char*)"AT+ATD*99#\r\n",
	.cmdSize = sizeof("AT+ATD*99#\r\n"),
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 2000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_CIFSR =
{
	.cmd = (char*)"AT+CIFSR\r\n",
	.cmdSize = sizeof("AT+CIFSR\r\n"),
	.cmdResponseOnOk = (char*)"",
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_CIPSTATUS =
{
	.cmd = (char*)"AT+CIPSTATUS\r\n",
	.cmdSize = sizeof("AT+CIPSTATUS\r\n"),
	.cmdResponseOnOk = (char*)"CONNECT OK",
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_CIPSHUT =
{
	.cmd = (char*)"AT+CIPSHUT\r\n",
	.cmdSize = sizeof("AT+CIPSHUT\r\n"),
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 2000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_CIPMUX =
{
	.cmd = (char*)"AT+CIPMUX=0\r\n",
	.cmdSize = sizeof("AT+CIPMUX=0\r\n"),
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 2000,
	.delayMs = 1000,
	.skip = 0,
};

static GSM_Cmd cmd_CIPSTART =
{
	.cmd = (char*)AT_CIPSTART,
	.cmdSize = sizeof(AT_CIPSTART),
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 3000,
	.delayMs = 5000,
	.skip = 0,
};

static GSM_Cmd cmd_SAPBRapn =
{
	.cmd = (char*)AT_SAPBR_APN,
	.cmdSize = sizeof(AT_SAPBR_APN)-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_SAPBRopen =
{
	.cmd = (char*)"AT+SAPBR=1,1\r\n",
	.cmdSize = sizeof("AT+SAPBR=1,1\r\n")-1,
	.cmdResponseOnOk = (char*)"",
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_SAPBRclose =
{
	.cmd = (char*)"AT+SAPBR=0,1\r\n",
	.cmdSize = sizeof("AT+SAPBR=0,1\r\n")-1,
	.cmdResponseOnOk = (char*)"",
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_HTTPINIT =
{
	.cmd = (char*)"AT+HTTPINIT\r\n",
	.cmdSize = sizeof("AT+HTTPINIT\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_HTTPSTATUS =
{
	.cmd = (char*)"AT+HTTPSTATUS?\r\n",
	.cmdSize = sizeof("AT+HTTPSTATUS?\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_HTTPPARAcid =
{
	.cmd = (char*)"AT+HTTPPARA=\"CID\",1\r\n",
	.cmdSize = sizeof("AT+HTTPPARA=\"CID\",1\r\n")-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_HTTPTERM =
{
	.cmd = (char*)"AT+HTTPTERM\r\n",
	.cmdSize = sizeof("AT+HTTPTERM\r\n")-1,
	.cmdResponseOnOk = (char*)"",//GSM_OK_Str,
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

static GSM_Cmd cmd_CIPCLOSE =
{
	.cmd = (char*)"AT+CIPCLOSE\r\n",
	.cmdSize = sizeof("AT+CIPCLOSE\r\n")-1,
	.cmdResponseOnOk = (char*)"",//GSM_OK_Str,//"CLOSE OK"
	.timeoutMs = 1000,
	.delayMs = 0,
	.skip = 0,
};

// #define CCLK "AT+CCLK=\"yyyyMMddhhmmss\"\r\n"
// #define CCLK "AT+CCLK=yyyyMMddhhmmss\r\n"
#define CCLK "AT+CCLK?\r\n"
static GSM_Cmd cmd_CCLK =
{
	.cmd = (char*)CCLK,
	.cmdSize = sizeof(CCLK)-1,
	.cmdResponseOnOk = (char*)GSM_OK_Str,
	.timeoutMs = 5000,
	.delayMs = 3000,
	.skip = 0,
};

static GSM_Cmd *GSM_Init[] =
{
	&cmd_AT,
	// &cmd_CIPSTATUS,
	&cmd_GSN,
	&cmd_CIMI,
	&cmd_Reset,
	&cmd_EchoOff,
	&cmd_RFOn,
	&cmd_NoSMSInd,
	&cmd_CPIN,
	&cmd_CREG,
	//&cmd_CGATT,
	&cmd_APN,
	//&cmd_CSTT,//set APN
	//&cmd_ATD,
	//&cmd_CIICR,
	//&cmd_CIFSR,
	&cmd_CIPSHUT,
	&cmd_CIPMUX,
	&cmd_CIPSTART,
	&cmd_CIPSTATUS,
	&cmd_SAPBRapn,
	// &cmd_SAPBRopen,
	// &cmd_HTTPINIT,
	// &cmd_HTTPPARAcid,
	// &cmd_HTTPTERM,
	// &cmd_SAPBRclose,
	// &cmd_CIPCLOSE
};

#define GSM_InitCmdsSize  (sizeof(GSM_Init)/sizeof(GSM_Cmd *))

static void infoCommand(char* cmd, int cmdSize, char* info)
{
	char buf[cmdSize+2];
	memset(buf, 0, cmdSize+2);
	for(int i=0; i<cmdSize;i++)
	{
		if((cmd[i] != 0x00) && ((cmd[i] < 0x20) || (cmd[i] > 0x7F))) buf[i] = '.';
		else buf[i] = cmd[i];
		if(buf[i] == '\0') break;
	}
	ESP_LOGI("GSM","%s [%s]", info, buf);
}

static int atCmd_waitResponse(char* cmd, char* resp, char* resp1, int cmdSize, int timeout, char** response, int size)
{
	char sresp[256] = {'\0'};
	char data[256] = {'\0'};
	int len, res = 1, idx = 0, tot = 0, timeoutCnt = 0;
	vTaskDelay(100 / portTICK_PERIOD_MS);
	uart_flush(uart_num);
	if(cmd != NULL)
	{
		if(cmdSize == -1) cmdSize = strlen(cmd);
		#if GSM_DEBUG
			infoCommand(cmd, cmdSize, (char*)"AT COMMAND:");
		#endif
		uart_write_bytes(uart_num, (const char*)cmd, cmdSize);
		uart_wait_tx_done(uart_num, 100 / portTICK_RATE_MS);
	}
	if(response != NULL)
	{
		char *pbuf = *response;
		len = uart_read_bytes(uart_num, (uint8_t*)data, 256, timeout / portTICK_RATE_MS);
		while(len > 0)
		{
			if((tot+len) >= size)
			{
				char* ptemp = (char*)realloc(pbuf, size+512);
				if(ptemp == NULL) return 0;
				size += 512;
				pbuf = ptemp;
			}
			memcpy(pbuf+tot, data, len);
			tot += len;
			response[tot] = '\0';
			len = uart_read_bytes(uart_num, (uint8_t*)data, 256, 100 / portTICK_RATE_MS);
		}
		*response = pbuf;
		return tot;
	}
	idx = 0;
	while(1)
	{
		memset(data, 0, 256);
		len = 0;
		len = uart_read_bytes(uart_num, (uint8_t*)data, 256, 10 / portTICK_RATE_MS);
		if(len > 0)
		{
			for(int i=0; i<len;i++)
			{
				if(idx < 256)
				{
					if((data[i] >= 0x20) && (data[i] < 0x80)) sresp[idx++] = data[i];
					else sresp[idx++] = 0x2e;
				}
			}
			tot += len;
		}
		else
		{
			if(tot > 0)
			{
				if(strstr(sresp, resp) != NULL)
				{
					#if GSM_DEBUG
						ESP_LOGI("GSM","AT RESPONSE: [%s]", sresp);
					#endif
					break;
				}
				else
				{
					if(resp1 != NULL)
					{
						if(strstr(sresp, resp1) != NULL)
						{
							#if GSM_DEBUG
								ESP_LOGI("GSM","AT RESPONSE (1): [%s]", sresp);
							#endif
							res = 2;
							break;
						}
					}
					// no match
					#if GSM_DEBUG
						ESP_LOGI("GSM","AT BAD RESPONSE: [%s]", sresp);
					#endif
					res = 0;
					break;
				}
			}
		}
		timeoutCnt += 10;
		if(timeoutCnt > timeout)
		{
			#if GSM_DEBUG
				ESP_LOGE("GSM","AT: TIMEOUT");
			#endif
			res = 0;
			break;
		}
	}
	return res;
}

static void _disconnect(uint8_t rfOff)
{
	int res = atCmd_waitResponse((char*)"AT\r\n", (char*)GSM_OK_Str, NULL, 4, 1000, NULL, 0);
	if(res == 1)
	{
		if(rfOff)
		{
			cmd_CREG.timeoutMs = 10000;
			res = atCmd_waitResponse((char*)"AT+CFUN=4\r\n", (char*)GSM_OK_Str, NULL, 11, 10000, NULL, 0); // disable RF function
		}
		return;
	}
	#if GSM_DEBUG
		ESP_LOGI("GSM","ONLINE, DISCONNECTING...");
	#endif
	vTaskDelay(1000 / portTICK_PERIOD_MS);
	uart_flush(uart_num);
	uart_write_bytes(uart_num, "+++", 3);
	uart_wait_tx_done(uart_num, 10 / portTICK_RATE_MS);
	vTaskDelay(1000 / portTICK_PERIOD_MS);
	int n = 0;
	res = atCmd_waitResponse((char*)"ATH\r\n", (char*)GSM_OK_Str, (char*)"NO CARRIER", 5, 3000, NULL, 0);
	while (res == 0)
	{
		n++;
		if (n > 10)
		{
			#if GSM_DEBUG
				ESP_LOGI("GSM","STILL CONNECTED.");
			#endif
			n = 0;
			vTaskDelay(1000 / portTICK_PERIOD_MS);
			uart_flush(uart_num);
			uart_write_bytes(uart_num, "+++", 3);
			uart_wait_tx_done(uart_num, 10 / portTICK_RATE_MS);
			vTaskDelay(1000 / portTICK_PERIOD_MS);
		}
		vTaskDelay(100 / portTICK_PERIOD_MS);
		res = atCmd_waitResponse((char*)"ATH\r\n", (char*)GSM_OK_Str, (char*)"NO CARRIER", 5, 3000, NULL, 0);
	}
	vTaskDelay(100 / portTICK_PERIOD_MS);
	if (rfOff)
	{
		cmd_CREG.timeoutMs = 10000;
		res = atCmd_waitResponse((char*)"AT+CFUN=4\r\n", (char*)GSM_OK_Str, NULL, 11, 3000, NULL, 0);
	}
	#if GSM_DEBUG
	ESP_LOGI("GSM","DISCONNECTED.");
	#endif
}

static void enableAllInitCmd()
{
	for(int idx = 0; idx < GSM_InitCmdsSize; idx++)
		GSM_Init[idx]->skip = 0;
}

int gsm_RFOff()
{
	if(gsm_status != GSM_STATE_IDLE) return 0;
	uint8_t f = 1;
	char buf[64] = {'\0'};
	char *pbuf = buf;
	int res = atCmd_waitResponse((char*)"AT+CFUN?\r\n", NULL, NULL, -1, 2000, &pbuf, 63);
	if(res > 0){if(strstr(buf, "+CFUN: 4")) f = 0;}
	if(f){cmd_CREG.timeoutMs = 500; return atCmd_waitResponse((char*)"AT+CFUN=4\r\n", (char*)GSM_OK_Str, NULL, 11, 10000, NULL, 0);}
	return 1;
}
int gsm_RFOn()
{
	if (gsm_status != GSM_STATE_IDLE) return 0;
	uint8_t f = 1;
	char buf[64] = {'\0'};
	char *pbuf = buf;
	int res = atCmd_waitResponse((char*)"AT+CFUN?\r\n", NULL, NULL, -1, 2000, &pbuf, 63);
	if(res > 0){if(strstr(buf, "+CFUN: 1")) f = 0;}
	if(f){cmd_CREG.timeoutMs = 0; return atCmd_waitResponse((char*)"AT+CFUN=1\r\n", (char*)GSM_OK_Str, NULL, 11, 10000, NULL, 0);}
	return 1;
}

int gsm_init()
{
	bool wrong_response = false;
	int gsmCmdIter = 0;
	_disconnect(1); // Disconnect if connected
	enableAllInitCmd();
	gsm_status = GSM_STATE_DISCONNECTED;
	while(!gsm_status)
	{
		#if GSM_DEBUG
			ESP_LOGI("GSM","GSM initialization start");
		#endif
		vTaskDelay(500 / portTICK_PERIOD_MS);
		int nfail = 0;
		while(gsmCmdIter < GSM_InitCmdsSize)// * GSM Initialization loop
		{
			if (GSM_Init[gsmCmdIter]->skip)
			{
				#if GSM_DEBUG
					infoCommand(GSM_Init[gsmCmdIter]->cmd, GSM_Init[gsmCmdIter]->cmdSize, (char*)"Skip command:");
				#endif
				gsmCmdIter++;
				continue;
			}
			if(atCmd_waitResponse(GSM_Init[gsmCmdIter]->cmd, GSM_Init[gsmCmdIter]->cmdResponseOnOk, NULL, GSM_Init[gsmCmdIter]->cmdSize, GSM_Init[gsmCmdIter]->timeoutMs, NULL, 0) == 0)
			{
				wrong_response = true;
				if (GSM_Init[gsmCmdIter] == &cmd_CIPSTATUS)
				{
					_disconnect(1);
					enableAllInitCmd();
					gsmCmdIter = 0;
				}
				else if (GSM_Init[gsmCmdIter] == &cmd_HTTPINIT)
				{
					if(atCmd_waitResponse(cmd_HTTPTERM.cmd, cmd_HTTPTERM.cmdResponseOnOk, NULL, cmd_HTTPTERM.cmdSize, cmd_HTTPTERM.timeoutMs, NULL, 0) == 0)
					{
						_disconnect(1);
						enableAllInitCmd();
						gsmCmdIter = 0;
					}
					else
					{
						if(atCmd_waitResponse(cmd_HTTPINIT.cmd, cmd_HTTPINIT.cmdResponseOnOk, NULL, cmd_HTTPINIT.cmdSize, cmd_HTTPINIT.timeoutMs, NULL, 0) == 0)
						{
							_disconnect(1);
							enableAllInitCmd();
						}
						else
							wrong_response = false;
					}
				}
				else if (GSM_Init[gsmCmdIter] == &cmd_HTTPTERM)
				{
					_disconnect(1);
					enableAllInitCmd();
				}
				if(wrong_response)
				{
					#if GSM_DEBUG// * No response or not as expected, start from first initialization command
						ESP_LOGW("GSM","Wrong response, restarting...");
					#endif
					nfail++;
					if (nfail > 5)
					{
						#if GSM_DEBUG
							ESP_LOGE("GSM","NOT CONNECTED, CHECK ANTENNA OR SIM CARD!!!");
						#endif
						return gsm_status;
					}
					vTaskDelay(2000 / portTICK_PERIOD_MS);
					gsmCmdIter = 0;
					continue;
				}
			}
			if (GSM_Init[gsmCmdIter]->delayMs > 0) vTaskDelay(GSM_Init[gsmCmdIter]->delayMs / portTICK_PERIOD_MS);
 			/*if (GSM_Init[gsmCmdIter] != &cmd_CIPSTATUS)*/  GSM_Init[gsmCmdIter]->skip = 1;
			if (GSM_Init[gsmCmdIter] == &cmd_CREG) GSM_Init[gsmCmdIter]->delayMs = 0;
			gsmCmdIter++;// Next command
		}
		#if GSM_DEBUG
			ESP_LOGI("GSM","GSM initialized.");
		#endif
		gsm_status = GSM_STATE_CONNECTED;
	}
	return GSM_STATE_FIRSTINIT;
}


bool gsm_uart_init()
{
	if(gpio_set_direction((gpio_num_t)UART_GPIO_TX, GPIO_MODE_OUTPUT) != ESP_OK)
	{
	#if GSM_DEBUG
		ESP_LOGE("GSM","ERROR gpio_set_direction(UART_GPIO_TX=%d, GPIO_MODE_OUTPUT)", UART_GPIO_TX);
	#endif
	}
	else
	{
		if(gpio_set_direction((gpio_num_t)UART_GPIO_RX, GPIO_MODE_INPUT) != ESP_OK)
		{
		#if GSM_DEBUG
			ESP_LOGE("GSM","ERROR gpio_set_direction(UART_GPIO_RX=%d, GPIO_MODE_INPUT)", UART_GPIO_RX);
		#endif
		}
		else
		{
			if(gpio_set_pull_mode((gpio_num_t)UART_GPIO_RX, GPIO_PULLUP_ONLY) != ESP_OK)
			{
			#if GSM_DEBUG
				ESP_LOGE("GSM","ERROR gpio_set_pull_mode(UART_GPIO_RX=%d, GPIO_PULLUP_ONLY)", UART_GPIO_RX);
			#endif
			}
			else
			{
				uart_config_t uart_config = {.baud_rate = UART_BDRATE, .data_bits = UART_DATA_8_BITS, .parity = UART_PARITY_DISABLE, .stop_bits = UART_STOP_BITS_1, .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, .rx_flow_ctrl_thresh = 0};
				if(uart_param_config(uart_num, &uart_config) != ESP_OK)
				{
				#if GSM_DEBUG
					ESP_LOGE("GSM","ERROR uart_param_config(uart_num=%d, &uart_config)", uart_num);
				#endif
				}
				else
				{
					if(uart_set_pin(uart_num, UART_GPIO_TX, UART_GPIO_RX, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) != ESP_OK)
					{
					#if GSM_DEBUG
						ESP_LOGE("GSM","ERROR uart_set_pin(uart_num=%d, UART_GPIO_TX=%d, UART_GPIO_RX=%d, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE)", uart_num, UART_GPIO_TX, UART_GPIO_RX);
					#endif
					}
					else
					{
						if(uart_driver_install(uart_num, GSM_BUF_SIZE * 2, GSM_BUF_SIZE * 2, 0, NULL, 0) != ESP_OK)
						{
						#if GSM_DEBUG
							ESP_LOGE("GSM","ERROR uart_driver_install(%d, %d, %d, 0, NULL, 0)", uart_num, GSM_BUF_SIZE * 2, GSM_BUF_SIZE * 2);
						#endif
						}
						else
						{
							return true;
						}
					}
				}
			}
		}
	}
	return false;
}

bool gsm_http_reanimation()
{
	// #if GSM_DEBUG
		// ESP_LOGE("GSM","GSM_HTTP_REANIMATION");
	// #endif
	atCmd_waitResponse(cmd_SAPBRclose.cmd, cmd_SAPBRclose.cmdResponseOnOk, NULL, cmd_SAPBRclose.cmdSize, cmd_SAPBRclose.timeoutMs, NULL, 0);
	if(atCmd_waitResponse(cmd_SAPBRopen.cmd, cmd_SAPBRopen.cmdResponseOnOk, NULL, cmd_SAPBRopen.cmdSize, cmd_SAPBRopen.timeoutMs, NULL, 0) == 0)
		return (bool)gsm_init();
	else
		return true;
}

bool gsm_http_init()
{
	// #if GSM_DEBUG
		// ESP_LOGE("GSM","GSM_HTTP_INIT");
	// #endif
	bool connected = false;
	// cmd_CIPSHUT,
	// cmd_CIPMUX,
	// cmd_CIPSTART,
	// cmd_CIPSTATUS,
	// cmd_SAPBRapn,
	// cmd_CIPCLOSE
	if(atCmd_waitResponse(cmd_SAPBRopen.cmd, cmd_SAPBRopen.cmdResponseOnOk, NULL, cmd_SAPBRopen.cmdSize, cmd_SAPBRopen.timeoutMs, NULL, 0) == 0)
	{
		connected = gsm_http_reanimation();
	}
	// if(atCmd_waitResponse(cmd_HTTPSTATUS.cmd, cmd_HTTPSTATUS.cmdResponseOnOk, NULL, cmd_HTTPSTATUS.cmdSize, cmd_HTTPSTATUS.timeoutMs, NULL, 0) == 0)
	if(!connected)
	{
		if(atCmd_waitResponse(cmd_HTTPINIT.cmd, cmd_HTTPINIT.cmdResponseOnOk, NULL, cmd_HTTPINIT.cmdSize, cmd_HTTPINIT.timeoutMs, NULL, 0) == 0)
		{
			if(atCmd_waitResponse(cmd_HTTPTERM.cmd, cmd_HTTPTERM.cmdResponseOnOk, NULL, cmd_HTTPTERM.cmdSize, cmd_HTTPTERM.timeoutMs, NULL, 0) == 0)
			{
				connected = (bool)gsm_init();
			}
			else
			{
				if(atCmd_waitResponse(cmd_HTTPINIT.cmd, cmd_HTTPINIT.cmdResponseOnOk, NULL, cmd_HTTPINIT.cmdSize, cmd_HTTPINIT.timeoutMs, NULL, 0) == 0)
				{
					connected = (bool)gsm_init();
				}
				else
					connected = true;
			}
		}
		else
			connected = true;
	}
	if(connected) atCmd_waitResponse(cmd_HTTPPARAcid.cmd, cmd_HTTPPARAcid.cmdResponseOnOk, NULL, cmd_HTTPPARAcid.cmdSize, cmd_HTTPPARAcid.timeoutMs, NULL, 0);
	return connected;
}

bool gsm_send_http(int evt_type, char* data)
{
	bool not_sending = true;
	// #if GSM_DEBUG
		// ESP_LOGE("GSM","GSM_SEND_HTTP %d, %s", evt_type, data);
	// #endif
	bool connected = false;
	if(!gsm_status) connected = (bool)gsm_init();
	if(!connected) connected = gsm_http_init();
	// if(!connected)
	// {
		// #if GSM_DEBUG
			// ESP_LOGE("GSM","NOT CONNECTED, CHECK ANTENNA OR SIM CARD!!!");
		// #endif
	// }
	// else
	if(connected)
	{
		// test();
		char* at_answer = (char*)malloc(32);
		// char* pat_answer = at_answer;
		int trying = 3, idx = 0;
		char at_url[64];
		char at_data[32];
		int data_length = strlen(data);
		sprintf(at_url, "AT+HTTPPARA=\"URL\",\"%s/%s\"\r\n", WEB_URL_API, evt_type ? "data" : "beverage");
		sprintf(at_data, "AT+HTTPDATA=%d,10000\r\n", data_length);
		while(not_sending && idx < trying)
		{
			idx++;
			if(atCmd_waitResponse(at_url, (char*)GSM_OK_Str, NULL, sizeof(at_url), 1000, NULL, 0) == 0){gsm_http_init(); continue;}
			vTaskDelay(1000 / portTICK_PERIOD_MS);
			if(atCmd_waitResponse(at_data, (char*)"DOWNLOAD", NULL, sizeof(at_data), 1000, NULL, 0) == 0) continue;
			// if(atCmd_waitResponse(data, (char*)GSM_OK_Str, NULL, data_length, 5000, NULL, 0) == 0) continue;
			uart_write_bytes(uart_num, (const char*)data, data_length);
			vTaskDelay(2000 / portTICK_PERIOD_MS);
			if(atCmd_waitResponse((char*)"AT+HTTPACTION=1\r\n", (char*)GSM_OK_Str, NULL, sizeof("AT+HTTPACTION=1\r\n"), 3000, &at_answer, 0) == 0)
			{
				gsm_http_init();
				continue;
			}
			else if(strstr(at_answer, "601"))
				gsm_http_reanimation();
			#if GSM_DEBUG
				ESP_LOGI("GSM","AT RESPONSE: [%s]", at_answer);
			#endif
			vTaskDelay(1000 / portTICK_PERIOD_MS);
			atCmd_waitResponse(cmd_HTTPSTATUS.cmd, cmd_HTTPSTATUS.cmdResponseOnOk, NULL, cmd_HTTPSTATUS.cmdSize, cmd_HTTPSTATUS.timeoutMs, NULL, 0);
			if(atCmd_waitResponse(cmd_HTTPTERM.cmd, cmd_HTTPTERM.cmdResponseOnOk, NULL, cmd_HTTPTERM.cmdSize, cmd_HTTPTERM.timeoutMs, NULL, 0) == 0)
			{
				if(atCmd_waitResponse(cmd_HTTPTERM.cmd, cmd_HTTPTERM.cmdResponseOnOk, NULL, cmd_HTTPTERM.cmdSize, cmd_HTTPTERM.timeoutMs, NULL, 0) == 0)
				{
					atCmd_waitResponse(cmd_CIPCLOSE.cmd, cmd_CIPCLOSE.cmdResponseOnOk, NULL, cmd_CIPCLOSE.cmdSize, cmd_CIPCLOSE.timeoutMs, NULL, 0);
					not_sending = !(bool)gsm_init();
				}
			}
			atCmd_waitResponse(cmd_SAPBRclose.cmd, cmd_SAPBRclose.cmdResponseOnOk, NULL, cmd_SAPBRclose.cmdSize, cmd_SAPBRclose.timeoutMs, NULL, 0);
			not_sending = false;
		}
		free(at_answer);
	}
	return !not_sending;
}

long gsm_get_time()
{
	long result = 0;
	// char* at_answer = (char*)malloc(64);
	// atCmd_waitResponse(cmd_CCLK.cmd, cmd_CCLK.cmdResponseOnOk, NULL, cmd_CCLK.cmdSize, cmd_CCLK.timeoutMs, &at_answer, 0);
	atCmd_waitResponse(cmd_CCLK.cmd, cmd_CCLK.cmdResponseOnOk, NULL, cmd_CCLK.cmdSize, cmd_CCLK.timeoutMs, NULL, 0);
	// #if GSM_DEBUG
		// ESP_LOGE("GSM","AT RESPONSE: [%s]", at_answer);
	// #endif
	// for(int i = 0; i < sizeof(at_answer); i++)
		// result = (result << 8) | at_answer[i];
	// free(at_answer);
	return result;
}
