ESP32 ESP-IDF SmartConfig Wi-Fi Provisioning with Smartphone App

In this ESP-IDF user guide, we will learn to use ESP32 SmartConfig for Wi-Fi Provisioning. We will show you how to connect our ESP32 board to a Wi-Fi network using a smartphone application that uses ESP Touch protocol. ESP Touch Protocol works on SmartConfig technology created by Texas Instruments. The smart phone app is used to broadcast the network credentials from a smartphone, or a tablet, to an un-provisioned Wi-Fi device.

The advantage of using SmartConfig Wi-Fi Provisioning is that we don’t have to hard code our network credentials in program codes in order to connect to a Wi-Fi network. Using SmartConfig allows the users to easily configure the Wi-Fi credentials through the smartphone app with out specifying them in sketches. Only the smartphone application which uses ESP Touch protocol is required to broadcast the credentials to ESP32. We will use EspTouch: SmartConfig for ESP8266, ESP32 application available for android phones in this guide. In short this guide includes a brief introduction to ESP Touch Protocol and then setting up a project in ESP-IDF for SmartConfig for ESP32 with EspTouch: SmartConfig Application on our smartphone.

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

ESP-Touch Protocol

The following lines are taken from the official Espressif web site for ESP-Touch Protocol:

“Espressif Systems developed the ESP-TOUCH protocol to seamlessly configure Wi-Fi devices connecting to a router. This is particularly important to headless systems, due to their lack of a user interface.”

As mentioned previously, the ESP Touch Protocol makes use of SmartConfig in order to connect ESP32 microcontrollers to a W-Fi network. This is done using a smartphone app that works on this protocol. What happens is that in order to configure the device, you use a smartphone app with ESP Touch protocol on it. The password of the Wi-Fi network to which the smartphone is connected is acquired from the user. The user types the password, which is then encoded and the network credentials are sent to ESP32 in a packet format. This data packet containing the Wi-Fi SSID and password are received by ESP32, which then decodes it and connects to this Wi-Fi network. When the ESP32 connects with the network, mDNS multicasts the notification to the smartphone application about the network connection.

ESP32 SmartConfig with ESP-IDF

Lets demonstrate an example of SmartConfig written by kapilkedawat which is available on GitHub. We will create a project in VS Code using ESP-IDF extension where the ESP32 board will connect to an access point using ESP Touch Protocol. This example uses APIs of esp_smartconfig.h along with other Wi-Fi libraries to establish a successful network connection.

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 ‘SmartConfig.’ 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.

In the Extension, select ESP-IDF option:

ESP-IDF in VS Code New Project 2

We will click the ‘sample_project’ under the get-started tab. Now click ‘Create project using template sample_project.’

ESP-IDF in VS Code New Project 3

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

This opens the 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.

ESP32 SmartConfig ESP-IDF Code

Head over to the main.c file. The main folder contains the source code meaning the main.c file will be found here. Go to main > main.c and open it. Copy the code given below in that file and save it.

This is an example sketch for ESP32 SmartConfig which broadcasts the Wi-Fi credentials using an ESP Touch application.

/* Esptouch 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 <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"

/* FreeRTOS event group to signal when we are connected & ready to make a request */
static EventGroupHandle_t s_wifi_event_group;

/* The event group allows multiple bits for each event,
   but we only care about one event - are we connected
   to the AP with an IP? */
static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;
static const char *TAG = "smartconfig_example";

static void smartconfig_example_task(void * parm);

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) {
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        uint8_t rvd_data[33] = { 0 };

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i=0; i<33; i++) {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

static void initialise_wifi(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

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

    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );

    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_start() );
}

static void smartconfig_example_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }
}

void app_main(void)
{
    ESP_ERROR_CHECK( nvs_flash_init() );
    initialise_wifi();
}

How the Code Works?

Firstly, we will start by including the necessary libraries that includes the FreeRTOS libraries to generate delays, create tasks and event groups. The esp_smartconfig.h to access the SmartConfig APIs. Additionally, the 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 <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_wifi.h"
#include "esp_wpa2.h"
#include "esp_event.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "esp_netif.h"
#include "esp_smartconfig.h"

Next, we declare a variable ‘s_wifi-event_group’ to hold the created FreeRTOS event group. This FreeRTOS event group is used to signal when we are connected to the Wi-Fi and ready to make a request.

static EventGroupHandle_t s_wifi_event_group;

Then we define two bits for the events that are of importance to us. These include the bit for a successful Wi-Fi connection with an AP and another bit when the ESP32 sends acknowledgement to the smartphone of successful connection.

static const int CONNECTED_BIT = BIT0;
static const int ESPTOUCH_DONE_BIT = BIT1;

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, “smartconfig_example” is the tag that will be used while logging.

static const char *TAG = "smartconfig_example";

event_handler()

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) {
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
    } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        uint8_t rvd_data[33] = { 0 };

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);
        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i=0; i<33; i++) {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    }
}

Then we have the event_handler() function which handles the Wi-Fi events. This acts as a callback function when either Wi-Fi event, IP event or SC event occurs.

IP events can be one of the following types. But here we are using only one that is IP_EVENT_STA_GOT_IP. This IP event occurs when the ESP32 board connects with our access point and router assigns an IP address to it.

/** IP event declarations */
typedef enum {
    IP_EVENT_STA_GOT_IP,               /*!< station got IP from connected AP */
    IP_EVENT_STA_LOST_IP,              /*!< station lost IP and the IP is reset to 0 */
    IP_EVENT_AP_STAIPASSIGNED,         /*!< soft-AP assign an IP to a connected station */
    IP_EVENT_GOT_IP6,                  /*!< station or ap or ethernet interface v6IP addr is preferred */
    IP_EVENT_ETH_GOT_IP,               /*!< ethernet got IP from connected AP */
    IP_EVENT_ETH_LOST_IP,              /*!< ethernet lost IP and the IP is reset to 0 */
    IP_EVENT_PPP_GOT_IP,               /*!< PPP interface got IP */
    IP_EVENT_PPP_LOST_IP,              /*!< PPP interface lost IP */
} ip_event_t;

The SC event or the SmartConfig event has the following types. We are using SC_EVENT_SCAN_DONE, SC_EVENT_FOUND_CHANNEL, SC_EVENT_GOT_SSID_PSWD and SC_EVENT_SEND_ACK_DONE in the code.

/** Smartconfig event declarations */
typedef enum {
    SC_EVENT_SCAN_DONE,                /*!< ESP32 station smartconfig has finished to scan for APs */
    SC_EVENT_FOUND_CHANNEL,            /*!< ESP32 station smartconfig has found the channel of the target AP */
    SC_EVENT_GOT_SSID_PSWD,            /*!< ESP32 station smartconfig got the SSID and password */
    SC_EVENT_SEND_ACK_DONE,            /*!< ESP32 station smartconfig has sent ACK to cellphone */
} smartconfig_event_t;

Firstly, when the Wi-Fi events (WIFI_EVENT and WIFI_EVENT_STA_START) occur, the smartconfig_example_task is created.

 if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
        xTaskCreate(smartconfig_example_task, "smartconfig_example_task", 4096, NULL, 3, NULL);
    }

Else if WIFI_EVENT_STA_DISCONNECTED occurs when the ESP32 station disconnects from AP, then esp_wifi_connect() function is called. The CONNECTED_BIT is also cleared.

else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_DISCONNECTED) {
        esp_wifi_connect();
        xEventGroupClearBits(s_wifi_event_group, CONNECTED_BIT);

Now, if the ESP32 board is able to successfully connect with an AP, then it suggests that both the IP events (IP_EVENT and IP_EVENT_STA_GOT_IP) occurred. In this case, it sets the CONNECTED_BIT.

 else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
        xEventGroupSetBits(s_wifi_event_group, CONNECTED_BIT);
    } 

When the ESP32 station smartconfig has finished the scan for access points, then the ESP terminal prints the message “Scan done.” This happens when the SC_EVENT_SCAN_DONE event occurs.

else if (event_base == SC_EVENT && event_id == SC_EVENT_SCAN_DONE) {
        ESP_LOGI(TAG, "Scan done");
    }

When the ESP32 station smartconfig has found the channel of the target access point, then the ESP terminal prints the message “Found channel.” This happens when the SC_EVENT_FOUND_CHANNEL event occurs.

else if (event_base == SC_EVENT && event_id == SC_EVENT_FOUND_CHANNEL) {
        ESP_LOGI(TAG, "Found channel");

Moreover, if the ESP32 station smartconfig obtains the SSID and password, then the ESP terminal prints the message “Got SSID and password.” This happens when the SC_EVENT_GOT_SSID_PSWD event occurs.

else if (event_base == SC_EVENT && event_id == SC_EVENT_GOT_SSID_PSWD) {
        ESP_LOGI(TAG, "Got SSID and password");

Next, the SSID and password which is broadcasted to the ESP32 is received and saved in the arrays ‘ssid’ and ‘password’ respectively. These are then printed on the ESP-IDF terminal.

First, we create a pointer of the argument structure for SC_EVENT_GOT_SSID_PSWD event called ‘evt’ and the ‘wifi_confg’ which is the configuration data for ESP32 AP or STA. Similarly, empty arrays for ssid, password and received data are also defined which are filled with the parameters received from the argument structure for SC_EVENT_GOT_SSID_PSWD, ‘evt.’

        smartconfig_event_got_ssid_pswd_t *evt = (smartconfig_event_got_ssid_pswd_t *)event_data;
        wifi_config_t wifi_config;
        uint8_t ssid[33] = { 0 };
        uint8_t password[65] = { 0 };
        uint8_t rvd_data[33] = { 0 };

        bzero(&wifi_config, sizeof(wifi_config_t));
        memcpy(wifi_config.sta.ssid, evt->ssid, sizeof(wifi_config.sta.ssid));
        memcpy(wifi_config.sta.password, evt->password, sizeof(wifi_config.sta.password));
        wifi_config.sta.bssid_set = evt->bssid_set;
        if (wifi_config.sta.bssid_set == true) {
            memcpy(wifi_config.sta.bssid, evt->bssid, sizeof(wifi_config.sta.bssid));
        }

        memcpy(ssid, evt->ssid, sizeof(evt->ssid));
        memcpy(password, evt->password, sizeof(evt->password));
        ESP_LOGI(TAG, "SSID:%s", ssid);
        ESP_LOGI(TAG, "PASSWORD:%s", password);

Now it checks if the type of protocol is ‘SC_TYPE_ESPTOUCH_V2 which suggests that ESPTouch v2protocol is being used then the received data is printed in the terminal. The Wi-Fi disconnects by calling esp_wifi_disconnect. Then we assign our network’s SSID and password using esp_wifi_set_config(). This way the Wi-Fi driver gets configured with the AP network credentials and Wi-Fi mode. Then, esp_wifi_connect() is called to connect the ESP32 to the Wi-Fi whose credentials have just been configured.

        if (evt->type == SC_TYPE_ESPTOUCH_V2) {
            ESP_ERROR_CHECK( esp_smartconfig_get_rvd_data(rvd_data, sizeof(rvd_data)) );
            ESP_LOGI(TAG, "RVD_DATA:");
            for (int i=0; i<33; i++) {
                printf("%02x ", rvd_data[i]);
            }
            printf("\n");
        }

        ESP_ERROR_CHECK( esp_wifi_disconnect() );
        ESP_ERROR_CHECK( esp_wifi_set_config(WIFI_IF_STA, &wifi_config) );
        esp_wifi_connect();
    } 

Moreover, when the SmartConfig Events occur, both SC_EVENT and SC_EVENT_SEND_ACK_DONE then the ESPTOUCH_DONE_BIT is set. This suggests that the ESP32 station smartconfig has sent acknowledgement to the smartphone.

else if (event_base == SC_EVENT && event_id == SC_EVENT_SEND_ACK_DONE) {
        xEventGroupSetBits(s_wifi_event_group, ESPTOUCH_DONE_BIT);
    

initialise_wifi()

The initialise_wifi() function will be called inside the app_main() function to initialize Wi-Fi for ESP32.

static void initialise_wifi(void)
{
    ESP_ERROR_CHECK(esp_netif_init());
    s_wifi_event_group = xEventGroupCreate();
    ESP_ERROR_CHECK(esp_event_loop_create_default());
    esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
    assert(sta_netif);

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

    ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
    ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );

    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_start() );
}

We start off by creating a FreeRTOS event group to monitor the connection. This returns a handle to the event group.

s_wifi_event_group = xEventGroupCreate();

The ESP-IDF provides a useful macro called ESP_ERROR_CHECK() that will be used to check whether an error occurred or not. If the function that we pass inside it does not return ESP_OK, then we assert and log the line.

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 station mode. This API is used to initialize as well as register event handlers for the default interface which is station in our case. It creates a network interface instance binding Wi-Fi driver and TCP/IP stack. When a station is in the process of connecting to an AP, various processes automatically get handled through this function.

 esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta();
 assert(sta_netif);

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 three registers for the event handler instances that we set up. This way the application task can register for a callback that can listen for events for Wi-Fi, SmartConfig and TCP/IP. The first event is ‘ESP_EVENT_ANY_ID‘ for WIFI_EVENT, the second event is ‘IP_EVENT_STA_GOT_IP’ for IP_EVENT and the third event is ‘ESP_EVENT_ANY_ID‘ for SC_EVENT. Therefore it will listen to all Wi-Fi and SC events (ESP_EVENT_ANY_ID) and an IP event when the station got its IP address (IP_EVENT_STA_GOT_IP). Hence, the callback event_handler function will be called if any of these three events occur.

ESP_ERROR_CHECK( esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &event_handler, NULL) );
ESP_ERROR_CHECK( esp_event_handler_register(SC_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL) );

Moreover, we set Wi-Fi mode as station. To set the Wi-Fi mode as station, we use the function esp_wifi_set_mode() and pass the WiFI_MODE_STA as a parameter inside it.  Then, we start the Wi-Fi connection at our assigned configuration.

    ESP_ERROR_CHECK( esp_wifi_set_mode(WIFI_MODE_STA) );
    ESP_ERROR_CHECK( esp_wifi_start() );

smartconfig_example_task()


static void smartconfig_example_task(void * parm)
{
    EventBits_t uxBits;
    ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );
    smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );
    while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }
}

Inside the smartconfig_example_task(), we will first set the protocol type of SmartConfig by calling esp_smartconfig_set_type() function and specify the parameter as ‘SC_TYPE_ESPTOUCH’ which suggests that the protocol is ESPTOUCH.

ESP_ERROR_CHECK( esp_smartconfig_set_type(SC_TYPE_ESPTOUCH) );

Then we start the SmartConfig where we config the ESP device to connect to the access point. It takes in a single parameter cfg which is a pointer to the smartconfig start configure structure which is set to SMARTCONFIG_START_CONFIG_DEFAULT() so that start configuration is at default values. Here smartconfig_start_config_t is a structure that denotes the configuration parameters that are passed inside the esp_smartconfig_start() function.

   smartconfig_start_config_t cfg = SMARTCONFIG_START_CONFIG_DEFAULT();
    ESP_ERROR_CHECK( esp_smartconfig_start(&cfg) );

Inside the while loop, the application moves to a blocked state whereby it waits for either of the bits in the event group to be set. This happens after the SmartConfig is set and started. The application remains in the blocked state until either the Wi-Fi gets connected which is monitored by CONNECTED_BIT or the ESP32 station smartconfig sends acknowledgement to the smartphone which is monitored by ESPTOUCH_DONE_BIT.

Now if the event_handler() function sets the CONNECTED_BIT which suggests that the ESP32 was able to successfully connect with the AP, then the informational log will print that it got connected to the AP. If the event_handler() function sets the ESPTOUCH_DONE_BIT which suggests that the ESP32 station smartconfig sent acknowledgement to the smartphone, then the informational log will print that smartconfig is over. The esp_smartconfig_stop() function will be called which stops SmartConfig by freeing the buffer taken by esp_smartconfig_start. The task will also be deleted.

while (1) {
        uxBits = xEventGroupWaitBits(s_wifi_event_group, CONNECTED_BIT | ESPTOUCH_DONE_BIT, true, false, portMAX_DELAY);
        if(uxBits & CONNECTED_BIT) {
            ESP_LOGI(TAG, "WiFi Connected to ap");
        }
        if(uxBits & ESPTOUCH_DONE_BIT) {
            ESP_LOGI(TAG, "smartconfig over");
            esp_smartconfig_stop();
            vTaskDelete(NULL);
        }
    }

app_main()

Inside the app_main() function, we will first initialize NVS by calling the nvs_flash_init() function. Then we call the initialise_wifi() function to initialize the Wi-Fi for station mode.

void app_main(void)
{
    ESP_ERROR_CHECK( nvs_flash_init() );
    initialise_wifi();
}

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

After the code flashes successfully, you can view all the informational logs. At this point, the ESP32 station smartconfig has finished the scan for access points, therefore the ESP terminal prints the message “Scan done.”

ESP32 SmartConfig ESP-IDF Terminal Scan done

Let us broadcast the network credentials to ESP32 by using the SmartConfig App. Install EspTouch: SmartConfig for ESP8266, ESP32 application as shown below:

It is an Espressif application that works on ESP Touch protocol which SmartConfig on ESP32/ESP8266.

Install ESP TOUCH Application for ESP32 SmartConfig

After the installation is complete, open the app and ensure that the smartphone is connected to the same Wi-Fi network which you want for ESP32. The app will retrieve the SSID of the connected network. If you are using an android phone, make sure you have turned on the location. Enter the password of the Wi-Fi network and select ‘Multicast’ option. Then click the Confirm button.

ESP32 SmartConfig Application Configuration 1

Shortly, the Esptouch will start configuring.

ESP32 SmartConfig Application Configuration 2

After the configuration is successfully completed, the app displays the Success message along with the BSSID and InetAddress.

ESP32 SmartConfig Application Configuration 3

The ESP-IDF terminal shows the following output where it finds the channels, obtains the SSID and password and connects to it.

ESP32 SmartConfig ESP-IDF Terminal ESP Touch Done

If you are using Arduino IDE instead of ESP-IDF, you can refer to this article:

2 thoughts on “ESP32 ESP-IDF SmartConfig Wi-Fi Provisioning with Smartphone App”

  1. Hello, great post! I am having some issues trying to get the credentials saved on flash.. could you tell me how to do that?
    Thanks!

    Reply
  2. HI, Any idea how I might interrupt smart config after a certain time period. If I use smartconfig on a bettery operated device and it fails to find the AP (for instance the network is down) then it sits forever waiting until the battery runs down. Similarly, if this is a “new” device it waits forever for the initial SSID/password. I would like it to time out so I can go to deep sleep for a certain period of time and then try again later.

    Thanks !

    Reply

Leave a Comment