ESP32 Wi-Fi Scan with ESP-IDF

In this tutorial, we will see how scan all nearby Access Points using ESP32 and ESP-IDF. We will display scanned APs RSSI values, Authentication type and channel. ESP-IDF provides an esp_wifi.h library that we use to perform scanning, connecting with access points and monitor ESP32 Wi-Fi state using different events. This API includes configuration of both Access point and Wi-Fi station modes of ESP32 in various security modes e.g. WPA, WEP, etc., monitoring of Wi-Fi packets and scanning nearby access points.

This Wi-Fi scan feature becomes useful when you have multiple access points and you want to connect with strongest AP. For example, you are using ESP32 in station mode. In station mode, ESP32 disconnects from your network due to any reason such network down, etc. In that situation, we can use Wi-Fi scan and connect ESP32 with other router or hotspot.

In previous guides, we looked upon how to setup our ESP32 as an access point and how to set it in station mode. Refer to the articles listed below:

This time however we will focus on Wi-Fi scan using the same ESP-IDF Wi-Fi driver. So let’s begin!

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 Scan with ESP-IDF

Lets create a sample project in ESP-IDF and program our ESP32 board with the Wi-Fi scan sketch. We will discuss the esp_wifi.h APIs that will be required for this project as well.

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 ‘ESP32_WIFI_SCAN.’ 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 Scan using ESP-IDF

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 our ESP32_WIFI_SCAN 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.

ESP-IDF ESP32 Wi-Fi Scan Code

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 and save it. The following code uses esp_wifi.h library functions to scan all nearby Wi-Fi networks along with their RSSI value, channel and authentication type.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_event_loop.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"

#define MAXIMUM_AP 20

static esp_err_t event_handler(void * ctx, system_event_t *event) {
  return ESP_OK;
}


static char *auth_mode_type(wifi_auth_mode_t auth_mode)
{
  char *types[] = {"OPEN", "WEP", "WPA PSK", "WPA2 PSK", "WPA WPA2 PSK", "MAX"};
  return types[auth_mode];
}

void wifiInit() {
  ESP_ERROR_CHECK(nvs_flash_init());
  tcpip_adapter_init();
  ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

  wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&wifi_config));
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  ESP_ERROR_CHECK(esp_wifi_start());

}

void app_main()
{
  wifiInit();

  wifi_scan_config_t scan_config = {
    .ssid = 0,
    .bssid = 0,
    .channel = 0,
    .show_hidden = true
  };

  ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true));

  wifi_ap_record_t wifi_records[MAXIMUM_AP];

  uint16_t max_records = MAXIMUM_AP;
  ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&max_records, wifi_records));

  printf("Number of Access Points Found: %d\n", max_records);
  printf("\n");
  printf("               SSID              | Channel | RSSI |   Authentication Mode \n");
  printf("***************************************************************\n");
  for (int i = 0; i < max_records; i++)
    printf("%32s | %7d | %4d | %12s\n", (char *)wifi_records[i].ssid, wifi_records[i].primary, wifi_records[i].rssi, auth_mode_type(wifi_records[i].authmode));
  printf("***************************************************************\n");
}

How the Code Works?

Firstly, we will start by including the necessary libraries that includes the esp_wifi.h for Wi-Fi functionality, esp_log.h as the logging library and the non-volatile storage (NVS) library.

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_event_loop.h"
#include "esp_wifi.h"
#include "esp_log.h"
#include "nvs_flash.h"

We have defined the maximum access points that can be scanned as 20.

#define MAXIMUM_AP 20

This function returns the authentication mode. The types included are: Open, WEP, WPA PSK, WPA2 PSK, WPA WPA2 PSK and MAX.

static char *auth_mode_type(wifi_auth_mode_t auth_mode)
{
  char *types[] = {"OPEN", "WEP", "WPA PSK", "WPA2 PSK", "WPA WPA2 PSK", "MAX"};
  return types[auth_mode];
}

wifiInit()

void wifiInit() {
  ESP_ERROR_CHECK(nvs_flash_init());
  tcpip_adapter_init();
  ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

  wifi_init_config_t wifi_config = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&wifi_config));
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  ESP_ERROR_CHECK(esp_wifi_start());

}

Inside the wifiInit() function, we will first initialize NVS and the underlying TCP/IP stack by calling nvs_flash_init() and tcpip_adapter_init() functions respectively.

ESP_ERROR_CHECK(nvs_flash_init());
tcpip_adapter_init();

To start the event loop implementation, we call the function esp_event_loop_init(). This function takes in two parameters. The first parameter is the pointer to the event handler function and the second parameter is an arbitrary context pointer.

ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));

This is the event_handler() function

static esp_err_t event_handler(void * ctx, system_event_t *event) {
  return ESP_OK;
}

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 wifi_config 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 wifi_config = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&wifi_config));

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.

ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));

Then, we start the Wi-Fi connection at our assigned configuration using esp_wifi_start().

ESP_ERROR_CHECK(esp_wifi_start());

app_main()

Inside the app_main() function, we will first call wifiInit() function to initialize the Wi-Fi connection and start it.

wifiInit();

Next, we call the function esp_wifi_scan_start() to scan all the nearby access points. This function takes in two parameters.

  • The first parameter is the configuration of scanning that is defined by the wifi_scan_config_t structure. It holds different scanning configuration parameters including SSID ,BSSID, Channel and show hidden APs.
  • The second parameter is the variable ‘block’ of type bool. If it is set as true then the caller will be blocked until the scan completes, otherwise the function returns immediately.
  wifi_scan_config_t scan_config = {
    .ssid = 0,
    .bssid = 0,
    .channel = 0,
    .show_hidden = true
  };

  ESP_ERROR_CHECK(esp_wifi_scan_start(&scan_config, true));

To obtain the list of access points that were scanned in the last scan, we call the function esp_wifi_scan_get_ap_records(). This function takes in two parameters.

  • The first parameter can act as an input an d an output parameter. As an input parameter it holds the maximum number of access points. As an output parameter, it obtains the AP number which this API returns.
  • The second parameter is the ‘ap_records’ which is ‘wifi_records’ in our case. It is an array of type wifi_ap_record_t that holds the access points that were scanned.
  wifi_ap_record_t wifi_records[MAXIMUM_AP];

  uint16_t max_records = MAXIMUM_AP;
  ESP_ERROR_CHECK(esp_wifi_scan_get_ap_records(&max_records, wifi_records));

At the point, the ESP-IDF prints the number of access points found which are held in the ‘max_records’ variable. After that it prints the details of the nearby networks that got scanned. This includes the SSID, Channel, RSSI and Authentication mode.

  printf("Number of Access Points Found: %d\n", max_records);
  printf("\n");
  printf("               SSID              | Channel | RSSI |   Authentication Mode \n");
  printf("***************************************************************\n");
  for (int i = 0; i < max_records; i++)
    printf("%32s | %7d | %4d | %12s\n", (char *)wifi_records[i].ssid, wifi_records[i].primary, wifi_records[i].rssi, auth_mode_type(wifi_records[i].authmode));
  printf("***************************************************************\n");

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 your code flashes successfully, the ESP-IDF terminal prints the number of access points scanned and a list showing various parameters. In our case, 5 access points were scanned nearby. The SSID, channel, RSSI level and authentication mode of each network can be seen as well.

ESP32 Wi-Fi Scan using ESP-IDF Terminal

You may also like to read:

Leave a Comment