Setup ESP32 as WiFi Station (ESP-IDF)

Introduction

In this article, you will learn steps to write a program to configure ESP32 as a WiFi station that can connect to a WiFi access point (AP) using ESP-IDF framework. We will analyse an example from ESP-IDF to understand how to write robust WiFi application. ESP-IDF provides an example to demonstrate how to configure ESP32 as a station in the folder

~/esp-idf/examples/wifi/getting_started/station

In this example project, you configure ESP32 as a station and attempt to connect to an AP. ESP32 will try to connect to the AP with known SSID and password. If it can successfully connect to the AP, it prints a success message on console. If it fails to connect, it will try a few times and print log to console.

Hardware Used

In this article, we will be using the hardware listed in the table below

QTYComponent NameBuy on amazon
1ESP32 DevKit Camazon.com

Affiliate Disclosure: When you click on links in this section and make a purchase, this may result in this site earning a commission at no extra cost to you.

Overall, when building a WiFi application with ESP32, you need to do several tasks

  • Initialisation
  • WiFi configuration
  • Start WiFi
  • Handle WiFi events

Let’s dive to each of these steps

Initialise WiFi

Before you can use WiFi, you must initialise WiFi and other network drivers to make WiFi work properly. There are a number of steps that need to be done.

Initialise NVS

NVS or Non-Volatile Storage is a partition in flash memory which stores key-value pairs. You can use NVS to store WiFi configuration. NVS is initialised by calling nvs_flash_init()

esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
   ESP_ERROR_CHECK(nvs_flash_erase());
   ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);

Initialise lwIP

Next initialise lwIP by calling esp_netif_init() API:

ESP_ERROR_CHECK(esp_netif_init());

There are a few things to note here. ESP-IDF uses lwIP, a lightweight TCP/IP library stack. lwIP implements protocols such as TCP, UDP, IP, DHCP, etc. ESP-NETIF is another library that encapsulates lwIP and provide another set of APIs. Using ESP-NETIF, in the future, it is possible to replace lwIP by another TCP/IP library and you don’t need to modify your ESP32 application code. ESP-NETIF is also thread safe. esp_netif_init() function performs lwIP initialisation and create lwIP task. To understand the role of lwIP and ESP-NETIF, take a look at the WiFi programming model in the below figure

ESP-IDF WiFi programming model. Source: Espressif

When building a WiFi application, you work on the application task. Behind the scene, other tasks are also created, including TCP/IP task (lwIP task), WiFi driver task, event task. The WiFi driver and lwIP task send events to the event task, and the event task notifies application task about those events.

Create default event loop

As seen in WiFi programming model, application task calls WiFi driver APIs to perform some jobs and listen to WiFi events delivered by an event task. The event loop library provides APIs to create an event task that:

  • Receive WiFi and TCP/IP events, such as when a station is connected to an AP, or when a station disconnect from an AP, or when a station has got its IP address
  • Allow application task to register a callback that will be executed when a WiFi or TCP/IP event happens.

The event task that receives WiFi and TCP/IP events are called default event loop. You need to create a default event loop so that system events can be sent to event task. This is accomplished by calling API

ESP_ERROR_CHECK(esp_event_loop_create_default());

This API allocates resources and creates system event task. Now the application task can register for a callback that listen for WiFi and TCP/IP events

esp_event_handler_instance_t instance_any_id;
esp_event_handler_instance_t instance_got_ip;
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                    ESP_EVENT_ANY_ID,
                                                    &event_handler,
                                                    NULL,
                                                    &instance_any_id));
ESP_ERROR_CHECK(esp_event_handler_instance_register(IP_EVENT,
                                                    IP_EVENT_STA_GOT_IP,
                                                    &event_handler,
                                                    NULL,
                                                    &instance_got_ip));

In this example, the application task wants to listen to all WiFi events and an IP event when the station got its IP address. The callback that will be executed is the function named event_handler with the following prototype

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    // Process events
}

Binding WiFi driver and esp-netif

The next step is to create a network interface instance binding WiFi driver and TCP/IP stack (esp-netif or lwIP). You do this by calling the function

esp_netif_create_default_wifi_sta();

What this function does is to bind esp-netif and the WiFi driver, which means register handlers so that esp-netif can process WiFi events. By calling this API, several processes when a station is connecting to an AP are handled automatically behind the scene. For instance, esp_netif will start DHCP client automatically when a station is connecting to an AP, and you don’t need to handle that.

Create WiFi driver task

The last step in initialisation is to create WiFi driver task and initialise WiFi driver which is shown this snippet

wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_wifi_init(&cfg));

Configure WiFi

Now you have done initialisation. Next step is to configure the WiFi driver, e.g. set SSID and password of the AP you want to connect to, configure ESP32 in station mode, etc.


wifi_config_t wifi_config = {
    .sta = {
        .ssid = EXAMPLE_ESP_WIFI_SSID,
        .password = EXAMPLE_ESP_WIFI_PASS,
        },
    },
};
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA) );
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );

In this example, you call esp_wifi_set_mode() and esp_wifi_set_config() APIs. You can set ESP32 in station mode by specifying WIFI_MODE_ST, AP mode WIFI_MODE_AP, or both station and AP at the same time WIFI_MODE_APSTA.

Start WiFi

All the heavy preparation steps have been completed by now. To start WiFi and connect to an AP, you just need to call

ESP_ERROR_CHECK(esp_wifi_start());

Handle WiFi events

As mentioned in previous section, the application task registers for an event handler to be called when WiFi or IP events happens. You can see the details of handling those events in this code

static void event_handler(void* arg, esp_event_base_t event_base,
                                int32_t event_id, void* event_data)
{
    if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        esp_wifi_connect();
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        if (s_retry_num < EXAMPLE_ESP_MAXIMUM_RETRY) {
            esp_wifi_connect();
            s_retry_num++;
            ESP_LOGI(TAG, "retry to connect to the AP");
        } else {
            xEventGroupSetBits(s_wifi_event_group, WIFI_FAIL_BIT);
        }
        ESP_LOGI(TAG,"connect to the AP fail");
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data;
        ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
        s_retry_num = 0;
        xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
    }
}

When the WiFi driver has been started, it sends an event WIFI_EVENT_STA_START, and calls esp_wifi_connect() to start connecting to the AP. If it is unable to connect, it will attempt to retry for a number of times before declaring fail. In this example, WiFi events are synchronised with application task using freeRTOS event groups. An event group has been declared

static EventGroupHandle_t s_wifi_event_group;

After initialising and starting WiFi, the application task goes to Blocked state waiting for bits in event group to be set

EventBits_t bits = xEventGroupWaitBits(s_wifi_event_group,
            WIFI_CONNECTED_BIT | WIFI_FAIL_BIT,
            pdFALSE,
            pdFALSE,
            portMAX_DELAY);

When ESP32 fails to connect to the AP, the event handler will set the WIFI_FAIL_BIT in the event group, which wakes the application task to print out fail message. If ESP32 connects to AP successfully and gets its IP address, the WIFI_CONNECTED_BIT bit in the event group is set, which unblock the application to print out its assigned IP.

Wrapping Up

In this post, you have learnt various steps in configuring ESP32 as a WiFi station using ESP-IDF. In the next post, we will take a look at how to turn ESP32 to an access point that other stations can connect to. Thanks for reading.

Leave a Comment