ESP32 Set an Access Point (AP) using ESP-IDF

In this user guide, we will learn to set up our ESP32 board in soft-AP or access point mode using ESP-IDF. This guide includes the explanations of the necessary APIs required for W-Fi connection, details about the Wi-Fi driver, and an example sketch for ESP-IDF demonstrating the ESP32 board acting as an access point and letting other devices connect with ESP32 AP.

We also have a guide to setup ESP32 in station mode using ESP-IDF:

Access Point (AP) Introduction

Soft-Access point/ AP mode is also known as a “software-enabled access point.” Acting as a ‘virtual router,’ a soft-access point combines software that sets up the ESP32 board into a wireless access point. As there is no connection with a wired network therefore it is known as soft-AP. As a physical router is not required, therefore it is advantageous to use ESP32 in soft-AP mode. In this mode, the ESP32 is able to generate its own wireless network where nearby Wi-Fi supported devices are able to connect to it, hence acting as a hot spot.

esp32 as an soft access point esp-idf

Before we move ahead, make sure you have the latest version of VS Code installed on your system with the ESP-IDF extension configured.

ESP32 Wi-Fi ESP-IDF APIs

Let us first discuss some important functions that are required for ESP32 to act as an Access Point. ESP-IDF provides esp_wifi.h library that is used to setup and monitor the working of the ESP32 Wi-Fi network. This includes configuration of both Access point and Wi-Fi client modes in various security modes e.g. WPA, WEP etc., monitoring of Wi-Fi packets and scanning nearby access points. In this guide, we will focus on how to connect stations to ESP32 whereby we will set up our ESP32 board in AP mode/Soft-AP mode/Access Point mode. Let’s see how it will be accomplished. 

The first step is to include the header file:

#include "esp_wifi.h"

Configuration

We start off by initializing the Wi-Fi allocate resource for the Wi-Fi driver. It is responsible for initiating the Wi-Fi task.

esp_wifi_init(const wifi_init_config_t *config)

It takes in a single parameter config which is a pointer to the already initialized Wi-Fi configuration structure. We usually set the Wi-Fi configuration structure to WIFI_INIT_CONFIG_DEFAULT() so that the initialization of the configuration is at default values. This is shown below:

wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();

Here wifi_init_config_t is a structure that denotes the Wi-Fi stack configuration parameters that are passed inside the esp_wifi_init() function.

Next up, we will set the mode of Wi-Fi. As mentioned earlier, this ESP-IDF Wi-Fi library enables multiple operating modes of Wi-Fi. To set the Wi-Fi mode, we use the function esp_wifi_set_mode() and pass the Wi-Fi mode as a parameter inside it. By default, the mode is set to station. We can set the mode to station, soft-AP or both (station + soft-AP). The parameter can take either of the three values:

  • WIFI_MODE_STA (for station mode)
  • WIFI_MODE_AP (for soft-AP mode)
  • WIFI_MODE_APSTA (for station + soft-AP mode)
esp_wifi_set_mode(wifi_mode_t mode)

Start Wi-Fi

In order to start the Wi-Fi connection at the set configuration, we call the function esp_wifi_start(). According to the configured mode, it first creates its control block and then initiates the mode.

esp_wifi_start(void)

To connect the ESP32 board set as a station with an access point, we use the function esp_wifi_connect(). This function is only applicable if the configured mode is station or station + soft-AP.

Note: This function is not applicable for soft-AP mode. It is not required.

esp_wifi_connect(void)

Features of ESP32 Wi-Fi

The ESP32 Wi-Fi supports four virtual Wi-Fi interfaces that include STA ,AP, Sniffer and reserved. Moreover, the ESP32 board can be configured in three modes that include station mode, soft-AP mode and both station and soft-AP mode together. Below we have stated some more features of ESP32 Wi-Fi.

  • The ESP-NOW protocol and Long Range mode supports a maximum of 1 km of data traffic.
  • It has a maximum of 20 MBit/s TCP throughput over the air.
  • Additionally, it features a maximum of 30 MBit/s UDP throughput over the air.
  • Coupled with various security standards including WPA/WPA2/WPA3/WPA2-Enterprise/WPA3-Enterprise/WAPI/WPS and DPP.
  • IEEE 802.11b, IEEE 802.11g, IEEE 802.11n, and APIs to configure the protocol mode.
  • AMSDU, AMPDU, HT40, QoS, modem sleep, multiple antennas.

ESP32 set up as soft-AP with ESP-IDF

In soft-AP mode, the ESP32 board acts as an access point that allows nearby devices to connect to it. Let’s look at an example sketch provided by ESP-IDF to learn how to set up an ESP32 as an AP.

Create Example Project

Open your VS Code and head over to View > Command Palette. Type ESP-IDF: New Project in the search bar and press enter.

Specify the project name and directory. We have named our project ‘ESP_IDF_WIFI_AP.’ For the ESP-IDF board, we have chosen the custom board option. For ESP-IDF target, we have chosen ESP32 module. Click ‘Choose Template’ button to proceed forward.

ESP32 Wi-Fi AP Mode ESP-IDF Project 1

In the Extension, select ESP-IDF option:

ESP-IDF in VS Code New Project 2

We will click the ‘softAP’ under the getting_started tab for wifi. Now click ‘Create project using template softAP.’

ESP32 Wi-Fi AP Mode ESP-IDF Project 2

You will get a notification that the project has been created. To open the project in a new window, click ‘Yes.’

This opens our ESP_IDF_WIFI_AP project that we created inside the EXPLORER tab. There are several folders inside our project folder. This is the same for every project which you will create through ESP-IDF Explorer. The main folder contains the source code meaning the main.c file will be found here.

Edit Kconfig File

First head over to main > Kconfig.projbuild

This file contains definitions of different parameters including the ESP_WIFI_SSID, ESP_WIFI_PASSWORD, ESP_WIFI_CHANNEL and ESP_MAX_STA_CONN. We can add more parameters as well according to our liking in between menu and endmenu keywords. You can set the values we have highlighted below in this file. It is recommended to set the network credentials for your ESP32 board that includes its SSID and password. Instead of hardcoding them in the main.c file, it is convenient to define these parameters here. Define them according to your preference and then save this file.

ESP32 Wi-Fi AP Mode ESP-IDF Project 3

Now go to main > softap_example_main.c and open it.

ESP32 Wi-Fi AP Mode ESP-IDF Project 4

The following code opens up which is shown below. This example first sets up the ESP32 board in AP mode and then allows stations to connect to it. By default, maximum 4 stations can connect to the ESP32 board. However, you can change that parameter in the Kconfig file as mentioned previously.

ESP-IDF Code: ESP32 setup as an Access Point

/*  WiFi softAP Example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"

#include "lwip/err.h"
#include "lwip/sys.h"

/* The examples use WiFi configuration that you can set via project configuration menu.

   If you'd rather not, just change the below entries to strings with
   the config you want - ie #define EXAMPLE_WIFI_SSID "mywifissid"
*/
#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_WIFI_CHANNEL   CONFIG_ESP_WIFI_CHANNEL
#define EXAMPLE_MAX_STA_CONN       CONFIG_ESP_MAX_STA_CONN

static const char *TAG = "wifi softAP";

static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                    int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}

void wifi_init_softap(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_ap();

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

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .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(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

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

void app_main(void)
{
    //Initialize NVS
    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);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
    wifi_init_softap();
}

How the Code Works?

Firstly, we will start by including the necessary libraries that includes the FreeRTOS libraries to generate delays, esp_wifi.h to enable Wi-Fi connectivity, esp_event.h to monitor the Wi-Fi events and esp_log.h as the logging library.

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

#include "lwip/err.h"
#include "lwip/sys.h"

Next, we have defined the parameters that we set in the Kconfig file. If you have already defined these parameters in that file then there is no need to hard code values here. However, if you want to define these parameters in the main.c file, then define them inside double quotation marks. For example: #define EXAMPLE_WIFI_SSID “ESP32-Soft-AP”

#define EXAMPLE_ESP_WIFI_SSID      CONFIG_ESP_WIFI_SSID
#define EXAMPLE_ESP_WIFI_PASS      CONFIG_ESP_WIFI_PASSWORD
#define EXAMPLE_ESP_WIFI_CHANNEL   CONFIG_ESP_WIFI_CHANNEL
#define EXAMPLE_MAX_STA_CONN       CONFIG_ESP_MAX_STA_CONN

This code uses Informational Logging. The log function takes in two arguments. The first argument is the tag and the second argument is a formatted string. Therefore this global variable will be useful while calling the ESP_LOGI() functions. Here, “wifi softAP” is the tag that will be used while logging.

static const char *TAG = "wifi softAP";

event_handler()

We have the event_handler() function which handles the Wi-Fi events. This acts as a callback function, when any Wi-Fi event occurs. There are a different events that the Wi-Fi driver posts to the event task. Two of them include: WIFI_EVENT_AP_STACONNECTED and WIFI_EVENT_AP_STADISCONNECTED. The first one occurs if a station gets connected to the AP. Likewise, the second one occurs if the station disconnects from the AP. In our event_handler() function, we will check if the Wi-Fi event id corresponds to either of these two events. If it does, then it will print the MAC address of the station in the terminal along with a relevant message according to the event type.

static void wifi_event_handler(void* arg, esp_event_base_t event_base,
                                    int32_t event_id, void* event_data)
{
    if (event_id == WIFI_EVENT_AP_STACONNECTED) {
        wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
                 MAC2STR(event->mac), event->aid);
    } else if (event_id == WIFI_EVENT_AP_STADISCONNECTED) {
        wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data;
        ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
                 MAC2STR(event->mac), event->aid);
    }
}

wifi_init_softap()

The wifi_init_softap() function will be called inside the app_main() function to initialize Wi-Fi in soft-AP mode for ESP32.

void wifi_init_softap(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_create_default_wifi_ap();

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

    ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

    wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .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(WIFI_IF_AP, &wifi_config));
    ESP_ERROR_CHECK(esp_wifi_start());

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

The ESP-IDF provides a useful macro called ESP_ERROR_CHECK() that will be used to check whether an error occurred or not.

Here we are initializing lwIP through the function esp_netif_init() and create an IwIP task which is also known as a TCP/IP task. lwIP is a TCP/IP library stack provided by ESP-IDF that is used to perform various protocols such as TCP, UDP, DHCP etc.

ESP_ERROR_CHECK(esp_netif_init());

Next, we create a default event loop which enables the system events to be sent to the event task.

ESP_ERROR_CHECK(esp_event_loop_create_default());

Moreover, the following line is used for Wi-Fi default initialization for AP mode. This API is used to initialize as well as register event handlers for the default interface which is AP in our case. It creates a network interface instance binding Wi-Fi driver and TCP/IP stack. This function is responsible for creating a esp_netif object with default Wi-Fi access point config, attaching the netif to Wi-Fi and registering default Wi-Fi handlers.

esp_netif_create_default_wifi_ap();
Configure Wi-Fi

Then we initialize the Wi-Fi allocate resource for the Wi-Fi driver. It is responsible for initiating the Wi-Fi task. It takes in a single parameter cfg which is a pointer to the already initialized Wi-Fi configuration structure which is set to WIFI_INIT_CONFIG_DEFAULT() so that the initialization of the configuration is at default values. Here wifi_init_config_t is a structure that denotes the Wi-Fi stack configuration parameters that are passed inside the esp_wifi_init() function.

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

Next, we have a register for the event handler instance. This way the application task can register for a callback that can listen for events for Wi-Fi. The event is ‘ESP_EVENT_ANY_ID.’ Therefore it will listen to all Wi-Fi events (ESP_EVENT_ANY_ID) and the callback event_handler function will be called if it occurs.

ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
                                                        ESP_EVENT_ANY_ID,
                                                        &wifi_event_handler,
                                                        NULL,
                                                        NULL));

In this part, we set up the SSID and password of the network we want our ESP32 to connect with, as well as assign the security setting for Wi-Fi configuration.

wifi_config_t wifi_config = {
        .ap = {
            .ssid = EXAMPLE_ESP_WIFI_SSID,
            .ssid_len = strlen(EXAMPLE_ESP_WIFI_SSID),
            .channel = EXAMPLE_ESP_WIFI_CHANNEL,
            .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;
    }

Moreover, we set Wi-Fi mode as AP. To set the Wi-Fi mode as AP, we use the function esp_wifi_set_mode() and pass the WiFI_MODE_AP as a parameter inside it.

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));

Then we assign our network’s SSID and password. This way the Wi-Fi driver gets configured with the AP network credentials and Wi-Fi mode.

ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
Start Wi-Fi

Then, we start the Wi-Fi connection at our assigned configuration.

ESP_ERROR_CHECK(esp_wifi_start() );

At this point the information log prints wifi_init_softap finished along with details of the AP’s SSID and password. We are done with the process of initializing the Wi-Fi in AP mode.

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

app_main()

Inside the app_main() function, we will first initialize NVS by calling the nvs_flash_init() function. Then we will check if the NVS partition does not contain any empty pages or if it contains data in a format which is different from the current version of code. If any of these condition is true then we will erase the NVS flash using nvs_flash_erase() and initialize it again.

Then we will print “ESP_WIFI_MODE_AP” in the terminal and call the wifi_init_softap() function to initialize the Wi-Fi for soft-AP mode.

void app_main(void)
{
    //Initialize NVS
    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);

    ESP_LOGI(TAG, "ESP_WIFI_MODE_AP");
    wifi_init_softap();
}

Compiling the Sketch

To flash your chip, type the following command in the serial terminal. Remember to replace the COM port with the one through which your board is connected.

idf.py -p COMX flash monitor
ESP32 Wi-Fi AP Mode ESP-IDF Project Flash

After the code flashes successfully, you can view all the informational logs.

We can first see “ESP_WIFI_MODE_AP” printed in the terminal. Followed by “wifi_init_softap finished” which denotes that the ESP32 board now acts as an access point.

ESP32 Wi-Fi AP Mode ESP-IDF Project Terminal 1

Now use your laptop or cell phone to connect with the ESP32 board’s Wi-Fi network. Open your Wi-Fi enabled device and go to Wi-Fi. Search for the available Wi-Fi networks. Here you will be able to see the SSID of ESP32 that you defined in the Kconfig file. In our case, we set the SSID as ‘ESP32-Soft-AP.’

ESP32 Wi-Fi AP Mode ESP-IDF Project demo 1

Click it and type the password that you set.

ESP32 Wi-Fi AP Mode ESP-IDF Project demo 2

Now Connect to this network. At this point, while the connection process is occurring the our ESP-IDF terminal shows the following logs:

ESP32 Wi-Fi AP Mode ESP-IDF Project Terminal 2

Once the station successfully connects with the ESP32 board acting as an AP, the log prints the MAC address of the station along with its assigned IP.

ESP32 Wi-Fi AP Mode ESP-IDF Project demo 3
ESP32 Wi-Fi AP Mode ESP-IDF Project Terminal 3

These are the informational logs that get printed when the soft-AP gets initialized for ESP32, the station starts to connect to the AP and when it successfully connects to it.

ESP32 Wi-Fi AP Mode ESP-IDF Project Terminal 5

Likewise, if the station disconnects from the AP, relevant messages are also printed as shown below:

ESP32 Wi-Fi AP Mode ESP-IDF Project Terminal 4

You may also like to read:

5 thoughts on “ESP32 Set an Access Point (AP) using ESP-IDF”

  1. Hi, when monitoring I see that i’m returning from app_main() after the AP is created, then it’s impossible for me to see any hotspot from other devices, any guess ? (i’m using esp32-c6

    Reply

Leave a Comment