(click to close)

ESP8266 WiFi Module - Building HTTP(S) Client

Overview

This chapter describes few key principles in order how to build your own HTTP REST Client application on ESP module. At present days more and more public data services are exposed under HTTP REST API standard. This way, if you would like to check the weather, either traffic congestion in your geographical area, either just to look into your own bitcoin wallet balance then you have to use HTTP REST API in various ways. Existing ESP8266 NONOS SDK V3.0 contains quite limited API capabilities to deal with HTTP requests and HTTP responses under REST API standard. So the purpose this section is to present several examples and code snippets which could help you to build your own custom ESP device capable of communicating with remote HTTP REST Services.

Youtube Video

The following video could help to understand how to onboard HTTP REST API Client capabilities into ESP application. It contains step-by-step instructions how to make ESP8266 firmware in Eclipse using C-language environment without any use of heavy frameworks like Arduino. This tutorial is built based on samples provided in previous part "ESP8266 Getting Started: C Programming in Eclipse using ESP SDK".




How to make HTTP Client integration into ESP application

A. New component on-boarding to ESP application compiler and linker stages.

Most probably, you already have worked with sample applications provided by ESP NONOS SDK. It is noticeable that majority of those applications are built there using "single module" design (usually main module called "user"). But in our case we would like to define new ESP app component within existing sample application. It will be used as a container for additional utils modules. These utilities will be responsible for HTTP communication and additional debug logging to serial port.

As the first step we have to create new folder "utils" with empty Makefile. This folder will contain new module source files:

Makefile definition will be quite trivial here. It will include content from parent script. Where parent Makefile will contain all of the logic to process and build new component. Only few variables needs to be defined here, such as GEN_LIB and PDIR (please refer parent project-level Makefile content for more details).

utils/Makefile

ifndef PDIR
GEN_LIBS = libutils.a
endif

INCLUDES := $(INCLUDES) -I $(PDIR)include
PDIR := ../$(PDIR)
sinclude $(PDIR)Makefile

Parent project-level Makefile also needs to be extended in order to allow compiler and linker to pick-up new modules. In general terms 3 sections needs to be updated:

•   SUBDIRS - Defines set of locations where components will be picked-up
•   COMPONENTS_eagle.app.v6 - Defines set of of archive files which needs to be included by linker into final output binary
•   LINKFLAGS_eagle.app.v6 - This section can be extended if some additional static libraries needs to be linked to assembly

Makefile

SUBDIRS= \
    user \
    utils
...

COMPONENTS_eagle.app.v6 = \
    user/libuser.a        \
    utils/libutils.a

LINKFLAGS_eagle.app.v6 =     \
    -L../lib                 \
    -nostdlib                \
    -T$(LD_FILE)             \
    -Wl,--no-check-sections  \
    -Wl,--gc-sections	     \
    -u call_user_start       \
    -Wl,-static              \
    -Wl,--start-group        \
    -lc                      \
    -lgcc                    \
    -lhal                    \
    -lphy                    \
    -lpp                     \
    -lnet80211               \
    -llwip                   \
    -lwpa                    \
    -lcrypto                 \
    -lmain                   \
    -ldriver                 \
    -lssl                    \
    -ljson                   \
    $(DEP_LIBS_eagle.app.v6) \
    -Wl,--end-group

Our sample contains LINKFLAGS linker section extended by additional -lssl and -ljson libraries. These extra libraries will be used here as we plan to employ SDK JSON parsing capabilities and use secure connections to support HTTPS transfer protocol.


B. Utility modules inclusion. Placing source code and header files into project.

Our base HTTP Client application will work with 2 supplementary modules:

•   mod_http - Provides base methods to operate with HTTP Responses. It also includes URLs handling and HTTP headers parsing logic.
•   mod_enums - Basic toolset for debugging and tracing in logs. Will be used to represent various ESP SDK enums as string values.

Here we have to append necessary *.h and *.c files into our project to make use of our new modules. All of these source code files are available at my GitHub repository https://github.com/sigma-prj/esp-mods. The following git clone command can be executed to get the latest snapshot:

git clone https://github.com/sigma-prj/esp-mods.git

Schema below indicates where these source code and header files needs to be included at our Eclipse project:

So you can copy-paste the following files (either take them from GitHub):

 include/mod_http.h

 utils/mod_http.c

 include/mod_enums.h

 utils/mod_enums.c


C. TCP - data transfer on ESP NONOS SDK. Concept of callbacks.

In order to transfer some data through TCP connection we have to follow specific steps. On each data transfer stage different issues might happen, so we have to make sure to handle all exceptional scenarios properly. Each stage is defined using "callback" methods mechanism. We already have covered callback sample in the previous part where we were creating "Blinkee" application:

void run_application(void* arg)
{
	if (GPIO_REG_READ(GPIO_OUT_ADDRESS) & (1 << GPIO_PIN_LED))
	{
		gpio_output_set(0, (1 << GPIO_PIN_LED), 0, 0);
	}
	else
	{
		gpio_output_set((1 << GPIO_PIN_LED), 0, 0, 0);
	}
}

void ICACHE_FLASH_ATTR user_init(void)
{
	uart_init(UART_BAUD_RATE, UART_BAUD_RATE);
	gpio_init();
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);
	gpio_output_set(0, 0, (1 << GPIO_PIN_LED), 0);
	os_timer_setfn(&start_timer, (os_timer_func_t*)run_application, NULL);
	os_timer_arm(&start_timer, 500, 1);
}

As you can see here os_timer_setfn method registers another run_application method as a callback. It means that run_application will be triggered at a later stage once the specific event will occur (in our case: 500ms repetitive timer trigger). The following specification indicates what are the major steps and callback registrations needs to be performed to process simple request\response TCP data transfer:

•   espconn_gethostbyname - Triggers DNS hostname resolution logic. This will provide resolved IP address based on input hostname.
•   espconn_regist_connectcb - Registers callback handler which is triggered on successful TCP connection
•   espconn_regist_reconcb - Registers callback handler which is triggered on failed attempt to establish TCP connection
•   espconn_connect - Invokes actual TCP connection establishment to specific address on certain port
•   espconn_regist_recvcb - Registers callback handler which is triggered when TCP data chunk is successfully received
•   espconn_regist_sentcb - Registers callback handler which is triggered when TCP data chunk is successfully sent
•   espconn_regist_disconcb - Registers callback handler which is triggered when TCP connection is closed
•   espconn_send - Triggers data transfer logic over established TCP connection
•   espconn_disconnect - Closes TCP connection

Steps above can be used to establish TCP connection to remote service and transfer some data using HTTP protocol as well. Also we might be interesting in data transferring to secure HTTPS services (alongside with HTTP services). In this case there is a need to use similar steps but with slightly bit different methods. These methods are supported by ESP NONOS SDK to maintain secure HTTPS connections. Also we need to remember that HTTPS connection usually goes to port 443 (instead of port 80, which HTTP uses by default). Table below indicates which corresponding methods needs to be updated to deal with secure HTTPS connection:

HTTP HTTPS
espconn_connect(espconn*) espconn_secure_connect(espconn*)
espconn_send(espconn*, uint8 *psent, uint16 length) espconn_secure_send(espconn*, uint8 *psent, uint16 length)
espconn_disconnect(espconn*) espconn_secure_disconnect(espconn*)

Before triggering espconn_secure_connect there is a need to make sure that relevant SNTP service initialization is done correctly:

#define SNTP_URL "pool.ntp.org"
...
sntp_setservername(0, SNTP_URL);
sntp_init();

The similar way sntp_stop() method needs to be triggered upon ESP connection is closed and you no longer planning to use HTTPS secure connection.

Picture below represents UML Sequence Diagram in respect of how ESP SDK callbacks can be organized to make a sample TCP message submission and response reception (click to enlarge):


D. Sample ESP application. HTTP Request composition. HTTP Response parsing. JSON parsing.

Typical client application will be reviewed here, which will demonstrate how is it possible to compose HTTP GET request and how to deal with corresponding HTTP response. So, the goal of this example is to connect to specific WiFi access point (using predefined WiFi session id and passphrase) and submit HTTP GET request to defined URL. Once HTTP response will be received this application will try to extract and print selected JSON tag from its body. Full source code can be found at the bottom of this section (here). But, before using that sample, there is a need to update following directives at user_main.c file:

// Update according to WiFi session ID
#define WIFI_SSID				"SSID"
// Update according to WiFi session password
#define WIFI_PASSPHRASE				"PASSWORD"
// HTTP(S) URL to query using HTTP GET method
#define HTTP_QUERY_URL				"URL"
// JSON tag name to extract
#define JSON_TAG_WEATHER_TEMPR			"temp"
// JSON tag depth level to extract
#define JSON_DEPTH_WEATHER_TEMPR		3


•   WIFI_SSID - Specifies WiFi session name to connect to
•   WIFI_PASSPHRASE - WiFi passphrase used by corresponding WiFi session
•   HTTP_QUERY_URL - Sample URL where HTTP GET request will be submitted to
•   JSON_TAG_WEATHER_TEMPR - Sample JSON tag name which will be extracted from HTTP response
•   JSON_DEPTH_WEATHER_TEMPR - Indicates JSON tag nested level number

Please note that JSON nested level number is defined using counting from 0. Picture below demonstrates how the actual JSON tag level is counted:

The similar way, as it was demonstrated in previous section ("ESP8266 Getting Started" video) - after application compilation and startup you can open ESP serial port and execute the following commands:

•   'S' - SCAN - Will try to scan all available WiFi networks and print extracted details against them
•   'C' - CONNECT - Will try to connect to predefined WiFi network
•   'D' - DISCONNECT - Will close current WiFi connection
•   'I' - INFO - Will print information about current connection state
•   'T' - TRANSFER - Will try to submit HTTP GET request and persist HTTP response at internal buffer
•   'P' - PARSE - Will try to parse JSON from internal buffer and extract specific tag

Following sample contains full client application source code:

 user/user_main.c

#include <osapi.h>
#include <mem.h>
#include <user_interface.h>
#include <gpio.h>
#include <espconn.h>
#include <sntp.h>
#include <json/jsonparse.h>

#include "mod_http.h"

// Update according to WiFi session ID
#define WIFI_SSID				"SSID"
// Update according to WiFi session password
#define WIFI_PASSPHRASE				"PASSWORD"
// HTTP(S) URL to query using HTTP GET method
#define HTTP_QUERY_URL				"https://openweathermap.org/data/2.5/weather?lat=51.905291&lon=4.466412&appid=439d4b804bc8187953eb36d2a8c26a02"
// JSON tag name to extract
#define JSON_TAG_WEATHER_TEMPR			"temp"
// JSON tag depth level to extract
#define JSON_DEPTH_WEATHER_TEMPR		3

#define UART_BAUD_RATE				115200
#define UART_LOCAL_RX_BUFFER_SIZE		128
#define LABEL_BUFFER_SIZE			128

#define SYSTEM_PARTITION_RF_CAL_SZ		0x1000
#define SYSTEM_PARTITION_PHY_DATA_SZ		0x1000
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_SZ	0x3000

#define SYSTEM_SPI_SIZE				0x400000

#define SYSTEM_PARTITION_RF_CAL_ADDR		SYSTEM_SPI_SIZE - SYSTEM_PARTITION_SYSTEM_PARAMETER_SZ - SYSTEM_PARTITION_PHY_DATA_SZ - SYSTEM_PARTITION_RF_CAL_SZ
#define SYSTEM_PARTITION_PHY_DATA_ADDR		SYSTEM_SPI_SIZE - SYSTEM_PARTITION_SYSTEM_PARAMETER_SZ - SYSTEM_PARTITION_PHY_DATA_SZ
#define SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR	SYSTEM_SPI_SIZE - SYSTEM_PARTITION_SYSTEM_PARAMETER_SZ

static const uint16 GPIO_PIN_LED = 2;
static const uint16 HEARTBEAT_FLASH_DELAY = 10 * 1000;
static os_timer_t start_timer;
static uint16 tick_index = 0;

// local_uart_rx_buf is used to store received input UART data
static uint8 local_uart_rx_buf[UART_LOCAL_RX_BUFFER_SIZE] = { 0 };
// index - used for cursor position tracking at receive buffer
static size_t local_http_receive_idx = 0;
// used to resolve target hostname ip address by DNS
static ip_addr_t target_server_ip;
// used to store url prefix type (HTTP or HTTPS)
static int url_prefix_type = HTTP_URL_HTTP;
// used to store http hostname
static char http_hostname[HTTP_HEADER_BUFFER_SIZE];
// used to store http path
static char http_path[HTTP_HEADER_BUFFER_SIZE];
// used to indicate whether SNTP service was activated
static bool is_sntp_active = false;
// used to indicate whether HTTP data transfer has been completed
static bool is_transfer_completed = false;
// this buffer is used to persist HTTP content
static char* http_content = NULL;
// actual connection definition used to perform HTTP GET request
struct espconn* pespconn = NULL;

static const partition_item_t part_table[] =
{
	{ SYSTEM_PARTITION_RF_CAL,		SYSTEM_PARTITION_RF_CAL_ADDR,			SYSTEM_PARTITION_RF_CAL_SZ		},
	{ SYSTEM_PARTITION_PHY_DATA,		SYSTEM_PARTITION_PHY_DATA_ADDR,			SYSTEM_PARTITION_PHY_DATA_SZ		},
	{ SYSTEM_PARTITION_SYSTEM_PARAMETER,	SYSTEM_PARTITION_SYSTEM_PARAMETER_ADDR,	        SYSTEM_PARTITION_SYSTEM_PARAMETER_SZ	}
};

// ##################################### SAMPLE COMMANDS #####################################

// ******************************** CONNECTION STATUS COMMAND *********************************

static bool is_station_connected(void)
{
	return wifi_station_get_connect_status() == STATION_GOT_IP;
}

static bool is_secure(void)
{
	return url_prefix_type == HTTP_URL_HTTPS;
}

static void connection_status(void)
{
	char label_status[LABEL_BUFFER_SIZE];
	uint8 status = wifi_station_get_connect_status();
	lookup_station_status(label_status, status);
	os_printf("\n[INFO] Current connection status: %s\n", label_status);
}

// *********************************** WIFI SCAN COMMAND ***********************************

static void ICACHE_FLASH_ATTR connection_scan_completed_callback(void *arg, STATUS status)
{
	int ret;
	char ssid_name[LABEL_BUFFER_SIZE];
	char ssid_cipher[LABEL_BUFFER_SIZE];
	if (status == OK)
	{
		struct bss_info *bss_link = (struct bss_info*)arg;
		while (bss_link != NULL)
		{
			if (bss_link->ssid_len)
			{
				os_memcpy(ssid_name, bss_link->ssid, bss_link->ssid_len);
				lookup_cipher(ssid_cipher, bss_link->group_cipher);
				ssid_name[bss_link->ssid_len] = 0;
				os_printf("\t- SSID: %s, channel: %d, freqcal_val: %d, freq_offset: %d, cipher: %s\n", ssid_name, bss_link->channel,
							bss_link->freqcal_val, bss_link->freq_offset, ssid_cipher);
			}
			bss_link = bss_link->next.stqe_next;
		}
		os_printf("[INFO] Scan has been completed successfully\n");
	} else {
		os_printf("[ERROR] Scan procedure has failed: %d\n", status);
	}
}

void scan_sessions(void)
{
	os_printf("\n[INFO] Scanning for available WiFi networks ...\n");
	wifi_station_scan(NULL, connection_scan_completed_callback);
}

// ******************************** WIFI CONNECT\DISCONNECT COMMANDS ********************************

void connection_configure(void)
{
	char ssid[] = WIFI_SSID;
	char password[] = WIFI_PASSPHRASE;
	struct station_config sta_conf = { 0 };

	os_memcpy(sta_conf.ssid, ssid, sizeof(ssid));
	os_memcpy(sta_conf.password, password, sizeof(password));
	wifi_station_set_config(&sta_conf);
	wifi_station_set_auto_connect(1);
}

void connect(void)
{
	if (!is_station_connected())
	{
		os_printf("\n[INFO] Connecting to predefined SSID ...\n");
		connection_configure();
		if (wifi_station_connect())
		{
			os_printf("[INFO] Command \"connect\" has been submitted\n");
		}
		else
		{
			os_printf("[ERROR] Unable to submit \"connect\" command\n");
		}
	}
	else
	{
		os_printf("\n[INFO] Already connected\n");
	}
}

void disconnect(void)
{
	os_printf("\n[INFO] Disconnecting from predefined SSID ...\n");
	wifi_station_set_auto_connect(0);
	if (wifi_station_disconnect())
	{
		os_printf("[INFO] Command \"disconnect\" has been submitted\n");
	}
	else
	{
		os_printf("[ERROR] Unable to submit \"disconnect\" command\n");
	}
}

// ******************************** COMMAND TO PERFORM SAMPLE HTTP REQUEST ********************************

// Forward-declarations

void release_espconn_memory(struct espconn* pconn);

// Callback methods

static void ICACHE_FLASH_ATTR on_dns_ip_resoved_callback(const char* hostnaname, ip_addr_t* ip, void* arg);
static void ICACHE_FLASH_ATTR on_tcp_connected_callback(void* arg);
static void ICACHE_FLASH_ATTR on_tcp_receive_data_callback(void* arg, char* user_data, unsigned short len);
static void ICACHE_FLASH_ATTR on_tcp_send_data_callback(void* arg);
static void ICACHE_FLASH_ATTR on_tcp_close_callback(void* arg);
static void ICACHE_FLASH_ATTR on_tcp_failed_callback(void* arg, sint8 error_type);

// ON IP ADDRESS RESOLVED BY HOSTNAME callback method

static void ICACHE_FLASH_ATTR on_dns_ip_resoved_callback(const char* hostnaname, ip_addr_t* ip, void* arg)
{
	struct espconn* pconn = (struct espconn*)arg;
	if (ip)
	{
		os_printf("[INFO] IP address by hostname `%s` is resolved: %d.%d.%d.%d\n",
				hostnaname,
				*((uint8*)&ip->addr),
				*((uint8*)&ip->addr+1),
				*((uint8*)&ip->addr+2),
				*((uint8*)&ip->addr+3));
		// TCP port configured to 80 (or 433) to make standard HTTP (or HTTPS) request
		if (is_secure())
		{
			pconn->proto.tcp->remote_port = 443;
		}
		else
		{
			pconn->proto.tcp->remote_port = 80;
		}
		// TCP IP address configured to value resolved by DNS
		os_memcpy(pconn->proto.tcp->remote_ip, &ip->addr, 4);
		espconn_regist_connectcb(pconn, on_tcp_connected_callback);
		espconn_regist_reconcb(pconn, on_tcp_failed_callback);
		char res_status[LABEL_BUFFER_SIZE];
		// Establishes TCP connection
		if (is_secure())
		{
			espconn_secure_set_size(0x01, TLS_HANDSHAKE_BUFFER_SIZE);
			sint8 res = espconn_secure_connect(pconn);
			lookup_espconn_error(res_status, res);
			os_printf("[INFO] Establishing secure TCP connection... %s\n", res_status);
		}
		else
		{
			sint8 res = espconn_connect(pconn);
			lookup_espconn_error(res_status, res);
			os_printf("[INFO] Establishing TCP connection... %s\n", res_status);
		}
	}
	else
	{
		os_printf("[ERROR] Unable get IP address by hostname `%s`\n", hostnaname);
		release_espconn_memory(pconn);
	}
}

// ON-SUCCESSFUL TCP CONNECT callback method (triggered upon TCP connection is established, but download has not stared yet)

static void ICACHE_FLASH_ATTR on_tcp_connected_callback(void* arg)
{
	os_printf("[INFO] TCP connection is established\n");
	struct espconn* pconn = (struct espconn*)arg;
	espconn_regist_disconcb(pconn, on_tcp_close_callback);
	espconn_regist_recvcb(pconn, on_tcp_receive_data_callback);
	espconn_regist_sentcb(pconn, on_tcp_send_data_callback);

	char tx_buf[HTTP_TX_BUFFER_SIZE];
	os_sprintf(tx_buf, "GET %s HTTP/1.1\r\nHost: %s\r\nAccept: */*\r\n\r\n", http_path, http_hostname);
	// os_printf("[DEBUG] HTTP TX buffer:\n%s\n", tx_buf);
	if (is_secure())
	{
		espconn_secure_send(pconn, tx_buf, os_strlen(tx_buf));
	}
	else
	{
		espconn_send(pconn, tx_buf, os_strlen(tx_buf));
	}
}

// ON-SUCCESSFUL TCP DISCONNECT callback method (triggered upon successful HTTP response download completed and socket connection is closed)

static void ICACHE_FLASH_ATTR on_tcp_close_callback(void* arg)
{
	os_printf("[INFO] TCP connection closed\n");
	struct espconn* pconn = (struct espconn*)arg;
	release_espconn_memory(pconn);
}

// ON-FAILED TCP CONNECT callback method (triggered in case of TCP connection cannot be established, used for re-try logic)

static void ICACHE_FLASH_ATTR on_tcp_failed_callback(void* arg, sint8 error_type)
{
	char error_info[LABEL_BUFFER_SIZE];
	lookup_espconn_error(error_info, error_type);
	os_printf("[ERROR] Failed to establish TCP connection: %s\n", error_info);
	struct espconn* pconn = (struct espconn*)arg;
	release_espconn_memory(pconn);
}

// TCP DATA RECEIVE callback method

static void ICACHE_FLASH_ATTR on_tcp_receive_data_callback(void* arg, char* user_data, unsigned short len)
{
	if (!is_transfer_completed)
	{
		os_printf("[DEBUG] On TCP data receive callback handler. Bytes received: %d.\n", len);
		char* local_content = (char*)os_malloc(local_http_receive_idx + len + 1);
		if (local_http_receive_idx > 0)
		{
			os_memcpy(local_content, http_content, local_http_receive_idx);
			os_free(http_content);
		}
		os_memcpy(&local_content[local_http_receive_idx], user_data, len);
		http_content = local_content;
		local_http_receive_idx += len;
		http_content[local_http_receive_idx] = 0;
		if (is_end_of_content(http_content))
		{
			local_http_receive_idx = 0;
			os_printf("[INFO] Full HTTP content has been received\n");
			// os_printf("[DEBUG] Received HTTP Content:\n%s\n", http_content);
			is_transfer_completed = true;
		}
	}
}

// TCP DATA SEND callback method

static void ICACHE_FLASH_ATTR on_tcp_send_data_callback(void* arg)
{
	os_printf("[INFO] On TCP data send callback handler\n");
}

// Releases ESP connection resources
void release_espconn_memory(struct espconn* pconn)
{
	if (pconn)
	{
		if (pconn->proto.tcp)
		{
			os_free(pconn->proto.tcp);
			pconn->proto.tcp = NULL;
		}
		os_printf("[INFO] TCP connection resources released\n");
		os_free(pconn);
		pespconn = NULL;
	}
}

// Actual HTTP request execution
void http_request(const char* url)
{
	// Memory allocation for pespconn
	pespconn = (struct espconn*)os_zalloc(sizeof(struct espconn));
	// ESP connection setup for TCP
	pespconn->type = ESPCONN_TCP;
	pespconn->state = ESPCONN_NONE;
	// Configuring ESP TCP settings
	pespconn->proto.tcp = (esp_tcp *)os_zalloc(sizeof(esp_tcp));
	// Performing basic URL parsing to extract hostname and HTTP path
	url_prefix_type = parse_url(HTTP_QUERY_URL, http_hostname, http_path);
	// Resolve IP address by hostname
	os_printf("[INFO] Trying to resolve IP address by hostname `%s` ...\n", http_hostname);
	// Clean HTTP Content loaded on previous submission
	if (http_content)
	{
		os_free(http_content);
		http_content = NULL;
	}
	espconn_gethostbyname(pespconn, http_hostname, &target_server_ip, on_dns_ip_resoved_callback);
}

// HTTP JSON Content Parsing
void process_content(void)
{
	if (http_content)
	{
		char* json_body = (char*)os_malloc(os_strlen(http_content));
		parse_http_body(http_content, json_body);
		os_printf("[INFO] JSON Body:\n%s\n\n", json_body);

		// JSON Parsing
		struct jsonparse_state parser;
		jsonparse_setup(&parser, json_body, os_strlen(json_body));
		int node_type;
		char value_buffer[LABEL_BUFFER_SIZE];
		os_bzero(value_buffer, LABEL_BUFFER_SIZE);
		bool result_found = false;
		while ((node_type = jsonparse_next(&parser)) != 0 && !result_found)
		{
			if (node_type == JSON_TYPE_PAIR_NAME && jsonparse_strcmp_value(&parser, JSON_TAG_WEATHER_TEMPR) == 0
											&& jsonparse_get_len(&parser) == os_strlen(JSON_TAG_WEATHER_TEMPR)
											&& parser.depth == JSON_DEPTH_WEATHER_TEMPR)
			{
				jsonparse_next(&parser);
				node_type = jsonparse_next(&parser);
				if (node_type == JSON_TYPE_NUMBER)
				{
					jsonparse_copy_value(&parser, value_buffer, sizeof(value_buffer));
					result_found = true;
				}
			}
		}

		if (result_found)
		{
			os_printf("[INFO] Parsed JSON Element: Temperature value: %s\n", value_buffer);
		}
		else
		{
			os_printf("[INFO] Unable to found JSON Element with temperature value\n");
		}
		os_free(json_body);
	}
	else
	{
		os_printf("[INFO] HTTP content is empty\n");
	}
}

// **************************************** COMMANDS DISPATCHER METHOD *************************************

void process_input_command(const uint8 cmd)
{
	switch(cmd)
	{
		case 's':
		case 'S':
			scan_sessions();
			break;
		case 'c':
		case 'C':
			connect();
			break;
		case 'd':
		case 'D':
			disconnect();
			break;
		case 'i':
		case 'I':
			connection_status();
			break;
		case 't':
		case 'T':
			os_printf("[INFO] Submitting sample HTTP GET request ...\n");
			http_request(HTTP_QUERY_URL);
			break;
		case 'p':
		case 'P':
			os_printf("[INFO] Trying to parse HTTP JSON content...\n");
			process_content();
			break;
	}
}

// ############################# APPLICATION MAIN LOOP METHOD (TRIGGERED EACH 50 MS) #############################

void run_application(void* arg)
{
	++tick_index;
	if (tick_index % 20 == 0)
	{
		if (is_station_connected())
		{
			// SNTP connection initialisation (used for TLS shared key generation)
			if (!is_sntp_active)
			{
				sntp_setservername(0, SNTP_URL);
				sntp_init();
				is_sntp_active = true;
			}
			// Build-in LED Heartbeat flashing - when WiFi connection established
			GPIO_OUTPUT_SET(GPIO_PIN_LED, 0);
			os_delay_us(HEARTBEAT_FLASH_DELAY);
			GPIO_OUTPUT_SET(GPIO_PIN_LED, 1);
		}
		else
		{
			// SNTP connection shutdown
			if (is_sntp_active)
			{
				sntp_stop();
				is_sntp_active = false;
			}
			// Build-in LED is switched-off when disconnected
			GPIO_OUTPUT_SET(GPIO_PIN_LED, 1);
		}
		tick_index = 0;
	}

	// Read input from UART0
	uint16 bytes_read = rx_buff_deq(local_uart_rx_buf, UART_LOCAL_RX_BUFFER_SIZE);
	if (bytes_read)
	{
		// Echo UART input back to user
		uart0_tx_buffer(local_uart_rx_buf, bytes_read);
		// Commands Processing
		uint16 i;
		for (i = 0U; i < bytes_read; ++i)
		{
			process_input_command(local_uart_rx_buf[i]);
		}
	}

	// Close TCP socket connection upon data transfer is completed
	if (is_transfer_completed)
	{
		is_transfer_completed = false;
		if (is_secure())
		{
			espconn_secure_disconnect(pespconn);
		}
		else
		{
			espconn_disconnect(pespconn);
		}
	}
}

// ##################################### APPLICATION MAIN INIT METHODS #####################################

// Used to extend memory by extra 17 KB of iRAM
uint32 user_iram_memory_is_enabled(void)
{
	return 1;
}

void ICACHE_FLASH_ATTR user_pre_init(void)
{
	system_partition_table_regist(part_table, 3, SPI_FLASH_SIZE_MAP);
}

void ICACHE_FLASH_ATTR user_init(void)
{
	uart_init(UART_BAUD_RATE, UART_BAUD_RATE);
	gpio_init();
	PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO2_U, FUNC_GPIO2);

	wifi_set_opmode(STATION_MODE);

	gpio_output_set(0, 0, (1 << GPIO_PIN_LED), 0);
	os_timer_setfn(&start_timer, (os_timer_func_t*)run_application, NULL);
	os_timer_arm(&start_timer, 50, 1);
}