Consulting Services
ArticlesEmbedded C & MCUIoT

All About ESP32 – Part 3

TCP and HTTP via WiFi

So far, the series has covered a good background to start digging for more in-depth details about ESP32. After the last part which was an introduction to the ESP-IDF WiFi driver, including application to scan for available APs and connect to one. This time, we will employee the established WiFi connection to make an HTTP request and TCP connection. However, a major part of programming WiFi as you’re going to see is related to socket programming in C using LwIP stack. To make life simpler, Espressif introduced a library to handle HTTP requests as a client in ESP-IDF.

 

LwIP Stack

 

ESP32 uses LwIP stack, thus the first way to make an HTTP connection or even pure TCP/UDP data transaction is to use LwIP API directly, where one of the LwIP ‘s application API layers is socket API which compatible with standard BSD-sockets. BSD socket programming in C is a broader topic, and this article will show the basics only.

I will borrow some parts of a very handy explanation of TCP/IP socket programming in C from BinaryTides website. The BinaryTides defined Sockets as “virtual” endpoints of any kind of network communications done between 2 hosts over a network. For example, when you type www.google.com in your web browser, it opens a socket and connects to google.com to fetch the page and show it to you. So the first thing to do is socket creation using socket() API method :

int socket(int domain, int type, int protocol);
  • domain argument selects the protocol family used for communication (.i.e AF_INET6  for IPv6 Internet protocols).
  • type argument could take SOCK_STREAM for TCP  or SOCK_DGRAM for UDP.
  • protocol argument according to Linux documentation: “specifies a particular protocol to be used with the socket. Normally only a single protocol exists to support a particular socket type within a given protocol family, in which case protocol can be specified as 0”.

Calling this function returns a number which is a file descriptor for the new socket and will be used in other functions.

So the first part of socket programming is:

int socket_desc;
    socket_desc = socket(AF_INET , SOCK_STREAM , 0);
     
    if (socket_desc == -1)
    {
        printf("Could not create socket");
    }

 

Server Side

 

Sockets need to be bound to an address and a port. The address could be a specific IP or not specific IP using INADDR_ANY option. The address and port can be specified using a struct sockaddr_in variable type.

struct sockaddr_in  server

//Prepare the sockaddr_in structure
server.sin_family = AF_INET;
server.sin_addr.s_addr = INADDR_ANY;
server.sin_port = htons( 8888 );
     
if( bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
{
    printf("bind failed");
}
printf("bind done");

The sockaddr_in struct from LwIP is:

struct sockaddr_in {
  u_short         sin_family;     /* always AF_INET                          */
  u_short         sin_port;       /* the service port                        */
  struct in_addr  sin_addr;       /* the IP address                          */
  char            sin_zero[8];    /* unused (reserved for expansion          */
};

The final initialization step to do is to call listen() API method to start listening for connections on the selected socket. This method takes 2 arguments, the first one is the socket descriptor and the second one specifies the maximum number of queued connections.

listen(socket_desc , 3);

Later, the accept() API method checks the queue for pending connections related to the socket with the specified file descriptor and returns a new file descriptor referring to that socket. The argument addr is a pointer to a sockaddr structure. This structure is filled in with the address of the peer socket.

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

You may later read or write data from/to that connection using read() or write() functions.

Putting all together:

while(1)
{
   printf("Waiting for incoming connections...");
    int new_socket, c = sizeof(struct sockaddr_in);
    new_socket = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (new_socket<0)
    {
            printf("accept failed");
    }
     else
{
char *message = "Hello Client , I have received your connection\n";
write(new_socket , message , strlen(message));

}
}

 

Client Side

 

If the socket is used to connect to a remote address (.i.e to connect to a website), the connect() API method is then used instead of bind() with the same arguments with no need to call listen() nor accept(). The following diagram associated with an answer on Stackoverflow clarify it:
ESP32 BSD Socket Creation

 

Software Access Point (SoftAP)

 

Here is another thing we need in this part’s applications, where one of the application will make ESP32 as an AP and a client will make a TCP connection via telnet/PuTTY.

To configure the ESP32 as an AP, the same functions used last time to access to an AP as a station will be used this time only with another wifi_config_t variable:

wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };

Where wifi_config_t has 2 parts:

typedef union {
    wifi_ap_config_t  ap;  /**< configuration of AP */
    wifi_sta_config_t sta; /**< configuration of STA */
} wifi_config_t;

Last time we used sta part from wifi_config_t, and this time we will use ap one. The initialization process of a SoftAP ESP32 will look like:

void wifi_init_softap()
{

    tcpip_adapter_init();
    esp_event_loop_init(event_handler, NULL);

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();

    esp_wifi_init(&cfg);

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };

    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    esp_wifi_set_mode(WIFI_MODE_AP);
    esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config);
    esp_wifi_start();

    ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
}

Now, let’s have a look at the event_handler(). Events related to SoftAP could be one of the following:

SYSTEM_EVENT_AP_START,                 /**< ESP32 soft-AP start */
    SYSTEM_EVENT_AP_STOP,                  /**< ESP32 soft-AP stop */
    SYSTEM_EVENT_AP_STACONNECTED,          /**< a station connected to ESP32 soft-AP */
    SYSTEM_EVENT_AP_STADISCONNECTED,       /**< a station disconnected from ESP32 soft-AP */

And this is an example handler. Check the previous part of this series to know how event_handler() works:

esp_err_t event_handler(void *ctx, system_event_t *event)
{

    tcpip_adapter_ip_info_t ip;

    switch(event->event_id) {

    case SYSTEM_EVENT_AP_STACONNECTED:

               ESP_LOGI(TAG,"station:"MACSTR"join”,MAC2STR(event->event_info.sta_connected.mac));
                tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip);    
                ESP_LOGI(TAG, "SoftAP IP=%s", inet_ntoa(ip.ip.addr));
}
        
        break;

    case SYSTEM_EVENT_AP_STADISCONNECTED:
        ESP_LOGI(TAG, "station:"MACSTR"leave",
                 MAC2STR(event->event_info.sta_disconnected.mac));
        break;
    default:
        break;
    }
    return ESP_OK;
}

system_event_t variable has information related to the connected/disconnected stations to ESP32 AP using event_info.sta_disconnected.mac for example.

Another thing to mention is tcpip_adapter_get_ip_info() API method which is used to get the network IP (or SoftAP IP). Finally, inet_ntoa() is a macro used to convert the numeric IP address into decimal dotted ASCII representation.

tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip);    
                ESP_LOGI(TAG, "SoftAP IP=%s", inet_ntoa(ip.ip.addr));

 

First App: HTTP Request

 

This example application will configure ESP32 as a station to get an internet access through the AP. After a successful connection to the AP, ESP32 will establish a socket and connect to the server. Keep in mind that this is a client-side connection so there is no need to use accept() nor listen() API methods.  

As the HTTP request will be for a specific website then we need a DNS to convert the domain name to an IP address. This is done by calling getaddrinfo() which will store the address information in a variable passed to it.

lwip_getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res);

The last thing to talk about before giving the complete code is the way to make add some special configurations from menuconfig menu, which is covered in the next section.

 

Special Configuration from Menuconfig

 

WiFi code, for instance, should be altered to use the right SSID/PWD for example which is mostly changed by defines from the source file. Another way to change these values is to enter them from menuconfig. A file in the source file directory named “Kconfig.projbuild” must be created with the following example content written in Kconfig language:

menu "Example Configuration"

config WIFI_SSID
    string "WiFi SSID"
    default "myssid"
    help
        SSID (network name) for the example to connect to.

config WIFI_PASSWORD
    string "WiFi Password"
    default "mypassword"
    help
        WiFi password (WPA or WPA2) for the example to use.

        Can be left blank if the network has no security set.

endmenu

Kconfig ESP32

Kconfig ESP32

In the code you should have defines in the same pattern like these:

#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD

Where the macro name could be anything but the value should be CONFIG_*** according to the name used in Kconfig.projbuild.

 

The Code

 

Now, it’s time to show the full code, and actually, it’s a modified and simplified version of original HTTP Request example from ESP-IDF:

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"



#define EXAMPLE_WIFI_SSID CONFIG_WIFI_SSID
#define EXAMPLE_WIFI_PASS CONFIG_WIFI_PASSWORD

static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;

#define WEB_SERVER "httpbin.org"
#define WEB_PORT 80
#define WEB_URL "http://httpbin.org"

static const char *TAG = "HTTP Request";

static const char *REQUEST = "GET " "/" " HTTP/1.0\r\n"
    "Host: "WEB_SERVER"\r\n"
    "User-Agent: esp-idf/1.0 esp32\r\n"
    "\r\n";

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch(event->event_id) {
    case SYSTEM_EVENT_STA_START:
        esp_wifi_connect();
        break;
    case SYSTEM_EVENT_STA_GOT_IP:
        xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
        break;
    case SYSTEM_EVENT_STA_DISCONNECTED:
        /* This is a workaround as ESP32 WiFi libs don't currently
           auto-reassociate. */
        esp_wifi_connect();
        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
        break;
    default:
        break;
    }
    return ESP_OK;
}

static void initialise_wifi(void)
{
    tcpip_adapter_init();
    wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK( esp_event_loop_init(event_handler, NULL) );
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_wifi_init(&cfg) );
    ESP_ERROR_CHECK( esp_wifi_set_storage(WIFI_STORAGE_RAM) );
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = EXAMPLE_WIFI_SSID,
            .password = EXAMPLE_WIFI_PASS,
        },
    };
    ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config) );
    ESP_ERROR_CHECK( esp_wifi_start() );
}

static void http_get_task(void *pvParameters)
{
    const struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *res;
    struct in_addr *addr;
    int socket_desc, r;
    char recv_buf[64];

    while(1) {
        xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
                            false, true, portMAX_DELAY);
        ESP_LOGI(TAG, "Connected to AP");

        socket_desc = socket(hints.ai_family, hints.ai_socktype, 0);
        
        if(socket_desc < 0) { ESP_LOGE(TAG, "... Failed to allocate socket."); freeaddrinfo(res); vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } ESP_LOGI(TAG, "... allocated socket"); int err = getaddrinfo(WEB_SERVER, "80", &hints, &res); if(err != 0 || res == NULL) { ESP_LOGE(TAG, "DNS lookup failed err=%d res=%p", err, res); vTaskDelay(1000 / portTICK_PERIOD_MS); continue; } addr = &((struct sockaddr_in *)res->ai_addr)->sin_addr;
        ESP_LOGI(TAG, "DNS lookup succeeded. IP=%s", inet_ntoa(*addr));


        if(connect(socket_desc, res->ai_addr, res->ai_addrlen) != 0) {
            ESP_LOGE(TAG, "... socket connect failed errno=%d", errno);
            close(socket_desc);
            freeaddrinfo(res);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }

        ESP_LOGI(TAG, "... connected");
        freeaddrinfo(res);

        if (write(socket_desc, REQUEST, strlen(REQUEST)) < 0) {
            ESP_LOGE(TAG, "... socket send failed");
            close(socket_desc);
            vTaskDelay(4000 / portTICK_PERIOD_MS);
            continue;
        }
        ESP_LOGI(TAG, "... socket send success");


        do {
            bzero(recv_buf, sizeof(recv_buf));
            r = read(socket_desc, recv_buf, sizeof(recv_buf)-1);
            for(int i = 0; i < r; i++) { putchar(recv_buf[i]); } } while(r > 0);

        ESP_LOGI(TAG, "... done reading from socket. Last read return=%d errno=%d\r\n", r, errno);
        close(socket_desc);
        for(int countdown = 10; countdown >= 0; countdown--) {
            ESP_LOGI(TAG, "%d... ", countdown);
            vTaskDelay(1000 / portTICK_PERIOD_MS);
        }
        ESP_LOGI(TAG, "Starting again!");
    }
}

void app_main()
{
    ESP_ERROR_CHECK( nvs_flash_init() );
    initialise_wifi();
    xTaskCreate(&http_get_task, "http_get_task", 4096, NULL, 5, NULL);
}

Second App: ESP HTTP Client (Another HTTP Request)

 

Dealing with HTTP is not as simple as the example discussed earlier. There are different operations involved, like authentication (both username/password or HTTPS), redirection, streaming, the GET/POST/DELETE/…etc requests. That’s why ESP-IDF presents an  API for making HTTP/S requests from ESP-IDF programs. Using ESP HTTP client API methods has 3 main steps:

1- define a variable with esp_http_client_config_t type and fill it with the required information including url. Examples:

  • Normal GET/POST requests:
esp_http_client_config_t config = {
        .url = "http://httpbin.org/get",
        .event_handler = _http_event_handler,
    };
  • Authentication by username and password:
esp_http_client_config_t config = {
        .url = "http://user:passwd@httpbin.org/basic-auth/user/passwd",
        .event_handler = _http_event_handler,
        .auth_type = HTTP_AUTH_TYPE_BASIC,
    };

2- Calling the esp_http_client_init() API method:

esp_http_client_handle_t client = esp_http_client_init(&config);

3- Calling esp_http_client_perform(), which actually “performs all operations of the esp_http_client, from opening the connection, sending data, downloading data and closing the connection if necessary” according to the official documentation.

Other functions are called according to the required operation. For example, for POST operation a call to esp_http_client_set_post_field(client, post_data, strlen(post_data)) is needed.

#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "freertos/event_groups.h"

#include "esp_http_client.h"


#define MAX_HTTP_RECV_BUFFER 512
static const char *TAG = "HTTP_CLIENT";

static EventGroupHandle_t wifi_event_group;


const int CONNECTED_BIT = BIT0;


static esp_err_t event_handler(void *ctx, system_event_t *event)
{
    switch (event->event_id) {
        case SYSTEM_EVENT_STA_START:
            esp_wifi_connect();
            break;
        case SYSTEM_EVENT_STA_GOT_IP:
            xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
            break;
        case SYSTEM_EVENT_STA_DISCONNECTED:
            /* This is a workaround as ESP32 WiFi libs don't currently
               auto-reassociate. */
            esp_wifi_connect();
            xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
            break;
        default:
            break;
    }
    return ESP_OK;
}


esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
    switch(evt->event_id) {
        case HTTP_EVENT_ERROR:
            ESP_LOGD(TAG, "HTTP_EVENT_ERROR");
            break;
        case HTTP_EVENT_ON_CONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_CONNECTED");
            break;
        case HTTP_EVENT_HEADER_SENT:
            ESP_LOGD(TAG, "HTTP_EVENT_HEADER_SENT");
            break;
        case HTTP_EVENT_ON_HEADER:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
            break;
        case HTTP_EVENT_ON_DATA:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
            if (!esp_http_client_is_chunked_response(evt->client)) {
                // Write out data
                // printf("%.*s", evt->data_len, (char*)evt->data);
            }

            break;
        case HTTP_EVENT_ON_FINISH:
            ESP_LOGD(TAG, "HTTP_EVENT_ON_FINISH");
            break;
        case HTTP_EVENT_DISCONNECTED:
            ESP_LOGD(TAG, "HTTP_EVENT_DISCONNECTED");
            break;
    }
    return ESP_OK;
}

static void http_rest()
{
    esp_http_client_config_t config = {
        .url = "http://random-quote-generator.herokuapp.com/api/quotes/random",
        .event_handler = _http_event_handler,
    };
    esp_http_client_handle_t client = esp_http_client_init(&config);

    // GET
    esp_err_t err = esp_http_client_perform(client);
    if (err == ESP_OK) {
        int content_length =  esp_http_client_get_content_length(client);
        ESP_LOGI(TAG, "HTTP GET Status = %d, content_length = %d",
                esp_http_client_get_status_code(client),
                content_length);
        char *buffer = malloc(MAX_HTTP_RECV_BUFFER);
        
        int read_len = esp_http_client_read(client, buffer, content_length);
        buffer[read_len] = 0;
        ESP_LOGI(TAG, "HTTP Stream reader Status = %d, content_length = %d, Buffer=%.*s",
                esp_http_client_get_status_code(client),
                read_len,
                read_len,
                buffer);
        free(buffer);
    } else {
        ESP_LOGE(TAG, "HTTP GET request failed: %s", esp_err_to_name(err));
    }


    esp_http_client_cleanup(client);
}

static void http_test_task(void *pvParameters)
{
        xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT, false, true, portMAX_DELAY);

    ESP_LOGI(TAG, "Connected to AP, begin http example");
    http_rest();
    ESP_LOGI(TAG, "Finish http example");
    vTaskDelete(NULL);
}

void app_wifi_initialise(void)
{
    tcpip_adapter_init();
    wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM));
    wifi_config_t wifi_config = {
        .sta = {
            .ssid = CONFIG_WIFI_SSID,
            .password = CONFIG_WIFI_PASSWORD,
        },
    };
    ESP_LOGI(TAG, "Setting WiFi configuration SSID %s...", wifi_config.sta.ssid);
    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

}

void app_main()
{
    esp_err_t ret = nvs_flash_init();
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
      ESP_ERROR_CHECK(nvs_flash_erase());
      ret = nvs_flash_init();
    }
    ESP_ERROR_CHECK(ret);
    app_wifi_initialise();

    xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL);
}

Here is the response to GET request to an online API for random quotes (http://random-quote-generator.herokuapp.com/api/quotes/random)

ESP32 Random qoute API

Another attempt to get the response to GET request to an online API for ASCII art. (http://artii.herokuapp.com/make?text=ASCII+art).

ESP32 ASCII ART API

To see other use-cases of ESP HTTP client, see the full example from ESP-IDF.

 

Third App: TCP Server

 

In this last application for this part, we will make a TCP server listen to TCP requests and answer it with a welcome message. In this application, we will print the connected stations IP to ESP32. The ESP32 will be a SoftAP and we will make the TCP connection using telnet on port 8888.

We already described in a past section how to make ESP32 in SoftAP mode, and in order to make the telnet connection we should know the network IP, thus we will print the network IP by calling tcpip_adapter_get_ip_info() which is also described previously.

Remember, as ESP32 here acts like a server then Socket creating will include calling bind() and listen() API methods.

Lastly, to print the IP address of connected stations, you can use this code:

wifi_sta_list_t wifi_sta_lis;
tcpip_adapter_sta_list_t tcpip_sta_list;
esp_wifi_ap_get_sta_list(&wifi_sta_lis);
tcpip_adapter_get_sta_list(&wifi_sta_lis, &tcpip_sta_list);

for (int i=0; i<wifi_sta_lis.num; i++){
                  printf("StaInfo:"MACSTR","IPSTR"",MAC2STR(wifi_sta_lis.sta[i].mac),IP2STR(&tcpip_sta_list.sta[i].ip));
}

The full code:

#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "tcpip_adapter.h"
#include <sys/socket.h>
#include "lwip/err.h"
#include "lwip/sys.h"
#include <netdb.h>


#define EXAMPLE_ESP_WIFI_MODE_AP   CONFIG_ESP_WIFI_MODE_AP //TRUE:AP FALSE:STA
#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_MAX_STA_CONN       CONFIG_MAX_STA_CONN

static EventGroupHandle_t wifi_event_group;

const int CONNECTED_BIT = BIT0;


static const char *TAG = "simple wifi";

static esp_err_t event_handler(void *ctx, system_event_t *event)
{
        wifi_sta_list_t wifi_sta_lis;
    tcpip_adapter_sta_list_t tcpip_sta_list;
    tcpip_adapter_ip_info_t ip;
    switch(event->event_id) {

    case SYSTEM_EVENT_AP_STACONNECTED:

                ESP_LOGI(TAG,"station:"MACSTR"join",MAC2STR(event->event_info.sta_connected.mac));
                
                 esp_wifi_ap_get_sta_list(&wifi_sta_lis);
                  tcpip_adapter_get_sta_list(&wifi_sta_lis, &tcpip_sta_list);

                  for (int i=0; i<wifi_sta_lis.num; i++){ printf("StaInfo:"MACSTR","IPSTR"",MAC2STR(wifi_sta_lis.sta[i].mac),IP2STR(&tcpip_sta_list.sta[i].ip)); tcpip_adapter_get_ip_info(TCPIP_ADAPTER_IF_AP, &ip); ESP_LOGI(TAG, "IP=%s", inet_ntoa(ip.ip.addr)); } xEventGroupSetBits(wifi_event_group, CONNECTED_BIT); break; case SYSTEM_EVENT_AP_STADISCONNECTED: ESP_LOGI(TAG, "station:"MACSTR"leave",MAC2STR(event->event_info.sta_disconnected.mac));
        xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
        break;
    default:
        break;
    }
    return ESP_OK;
}

static void http_get_task(void *pvParameters)
{
    const struct addrinfo hints = {
        .ai_family = AF_INET,
        .ai_socktype = SOCK_STREAM,
    };
    struct addrinfo *addr;
    int socket_desc,new_socket,c;
    struct sockaddr_in  server,client;

            xEventGroupWaitBits(wifi_event_group, CONNECTED_BIT,
                            false, true, portMAX_DELAY);
        ESP_LOGI(TAG, "SoftAP Started");

        socket_desc = socket(hints.ai_family, hints.ai_socktype, 0);
        
        if(socket_desc < 0) {
            ESP_LOGE(TAG, "... Failed to allocate socket.");
        }
        ESP_LOGI(TAG, "... allocated socket");
        
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = INADDR_ANY;
        server.sin_port = htons( 8888 );
        if(bind(socket_desc,(struct sockaddr *)&server , sizeof(server)) < 0)
        {
            ESP_LOGI(TAG,"bind failed");
        }
        ESP_LOGI(TAG,"bind done");
        
        //Listen
        listen(socket_desc , 3);
        
    while(1) {

    puts("Waiting for incoming connections...");
    c = sizeof(struct sockaddr_in);
    new_socket = accept(socket_desc, (struct sockaddr *)&client, (socklen_t*)&c);
    if (new_socket<0)
    {
        perror("accept failed");
        return ;
    }
     else
     {
    ESP_LOGI(TAG,"Connection accepted");
     
    char *message = "Hello Client , I have received your connection. But I have to go now, bye\n";
    write(new_socket , message , strlen(message));


    close(socket_desc);

    }
      vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}

void wifi_init_softap()
{
    wifi_event_group = xEventGroupCreate();

    tcpip_adapter_init();
    ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

    wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
    ESP_ERROR_CHECK(esp_wifi_init(&cfg));
    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .password = EXAMPLE_ESP_WIFI_PASS,
            .max_connection = EXAMPLE_MAX_STA_CONN,
            .authmode = WIFI_AUTH_WPA_WPA2_PSK
        },
    };
    if (strlen(EXAMPLE_ESP_WIFI_PASS) == 0) {
        wifi_config.ap.authmode = WIFI_AUTH_OPEN;
    }

    ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
    ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

    ESP_LOGI(TAG, "wifi_init_softap finished.SSID:%s password:%s",
             EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS);
}


void app_main()
{
    nvs_flash_init();  
    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
    xTaskCreate(&http_get_task, "http_get_task", 4096, NULL, 5, NULL);
    wifi_init_softap();
}

ESP32 TCP server response

Yahya Tawil

Embedded Hardware Engineer interested in open hardware and was born in the same year as Linux. Yahya is the editor-in-chief of Atadiat and believes in the importance of sharing free, practical, spam-free and high quality written content with others. His experience with Embedded Systems includes developing firmware with bare-metal C and Arduino, designing PCB&schematic and content creation.

2 Comments

  1. Thanks for these examples.
    Please be aware that the HTTP client sample lacks code (as do the examples in the ESP-IDF documentation).
    When your query has a large reply (e.g. 2500 bytes), the sample will only print the last part of the reply.
    Either speficy .buffer_size in the esp_http_client_config_t initialisation, or avoid use of esp_http_client_perform().
    This can easily be demonstrated with a query on wunderground.org .

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Back to top button